<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Websocket.+ Spring-SseEmitter3 实现讯飞星火Java客户端 | 鱼皮的编程宝典</title>
    <meta name="generator" content="VuePress 1.9.10">
    <link rel="icon" href="/favicon.ico">
    <script>
        var _hmt = _hmt || [];
        (function() {
          var hm = document.createElement("script");
          hm.src = "https://hm.baidu.com/hm.js?2675818a983a3131404cee835018f016";
          var s = document.getElementsByTagName("script")[0]; 
          s.parentNode.insertBefore(hm, s);
        })();
      </script>
    <meta name="description" content="贴心的编程学习路线，全面的编程知识百科">
    <meta property="article:modified_time" content="2023-11-22T01:15:50.000Z">
    <meta property="og:site_name" content="鱼皮的编程宝典">
    <meta property="og:title" content="Websocket.+ Spring-SseEmitter3 实现讯飞星火Java客户端">
    <meta property="og:type" content="article">
    <meta property="og:url" content="https://codefather.cn/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/">
    <meta name="twitter:title" content="Websocket.+ Spring-SseEmitter3 实现讯飞星火Java客户端">
    <meta name="twitter:url" content="https://codefather.cn/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/">
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:label1" content="Written by">
    <meta name="twitter:label2" content="Filed under">
    <meta name="twitter:data2" content="程序员, 编程, 计算机">
    <meta property="article:tag" content="程序员">
    <meta name="keywords" content="程序员鱼皮, 编程学习路线, 编程知识百科, Java, 编程导航, 前端, 开发, 编程分享, 项目, IT, 求职, 面经">
    
    <link rel="preload" href="/assets/css/0.styles.40c15e22.css" as="style"><link rel="preload" href="/assets/js/app.12ab4756.js" as="script"><link rel="preload" href="/assets/js/2.fd06b53e.js" as="script"><link rel="preload" href="/assets/js/73.d6ae6a27.js" as="script"><link rel="prefetch" href="/assets/js/1.17ea1209.js"><link rel="prefetch" href="/assets/js/10.d9532d8f.js"><link rel="prefetch" href="/assets/js/100.064ea126.js"><link rel="prefetch" href="/assets/js/101.d64c5b33.js"><link rel="prefetch" href="/assets/js/102.5e29a62c.js"><link rel="prefetch" href="/assets/js/103.59ad6cd7.js"><link rel="prefetch" href="/assets/js/104.ae4763e8.js"><link rel="prefetch" href="/assets/js/105.2a9124be.js"><link rel="prefetch" href="/assets/js/106.529bca07.js"><link rel="prefetch" href="/assets/js/107.c8671a0d.js"><link rel="prefetch" href="/assets/js/108.55c93c64.js"><link rel="prefetch" href="/assets/js/109.7d1efa54.js"><link rel="prefetch" href="/assets/js/11.2f89b5eb.js"><link rel="prefetch" href="/assets/js/110.0caecbf5.js"><link rel="prefetch" href="/assets/js/111.a2aeae49.js"><link rel="prefetch" href="/assets/js/112.eb7ceda5.js"><link rel="prefetch" href="/assets/js/113.f304408d.js"><link rel="prefetch" href="/assets/js/114.988c637f.js"><link rel="prefetch" href="/assets/js/115.bfdeed31.js"><link rel="prefetch" href="/assets/js/116.a47b8c53.js"><link rel="prefetch" href="/assets/js/117.2e1e8250.js"><link rel="prefetch" href="/assets/js/118.758ce408.js"><link rel="prefetch" href="/assets/js/119.73d9e688.js"><link rel="prefetch" href="/assets/js/12.53aaa509.js"><link rel="prefetch" href="/assets/js/120.202a8b2e.js"><link rel="prefetch" href="/assets/js/121.ca91d59a.js"><link rel="prefetch" href="/assets/js/122.8804c0dd.js"><link rel="prefetch" href="/assets/js/123.ceada225.js"><link rel="prefetch" href="/assets/js/124.4b2718b7.js"><link rel="prefetch" href="/assets/js/125.72134235.js"><link rel="prefetch" href="/assets/js/126.167b162c.js"><link rel="prefetch" href="/assets/js/127.f8139c17.js"><link rel="prefetch" href="/assets/js/128.da64a377.js"><link rel="prefetch" href="/assets/js/129.c07963b1.js"><link rel="prefetch" href="/assets/js/13.52940164.js"><link rel="prefetch" href="/assets/js/130.f66fc8bb.js"><link rel="prefetch" href="/assets/js/131.a913c7dd.js"><link rel="prefetch" href="/assets/js/132.ea648654.js"><link rel="prefetch" href="/assets/js/133.074ca70e.js"><link rel="prefetch" href="/assets/js/134.dc79ddd8.js"><link rel="prefetch" href="/assets/js/135.c4516208.js"><link rel="prefetch" href="/assets/js/136.2546baf7.js"><link rel="prefetch" href="/assets/js/137.56454fc6.js"><link rel="prefetch" href="/assets/js/138.bdb002bf.js"><link rel="prefetch" href="/assets/js/139.40a2f9b5.js"><link rel="prefetch" href="/assets/js/14.ade1e74d.js"><link rel="prefetch" href="/assets/js/140.06b20427.js"><link rel="prefetch" href="/assets/js/141.8dd992e3.js"><link rel="prefetch" href="/assets/js/142.d9305485.js"><link rel="prefetch" href="/assets/js/143.41bf907e.js"><link rel="prefetch" href="/assets/js/144.c138960c.js"><link rel="prefetch" href="/assets/js/145.fc5b38c8.js"><link rel="prefetch" href="/assets/js/146.c9166b70.js"><link rel="prefetch" href="/assets/js/147.c31d8a01.js"><link rel="prefetch" href="/assets/js/148.5c0534ca.js"><link rel="prefetch" href="/assets/js/149.d2355ea2.js"><link rel="prefetch" href="/assets/js/15.970a8ca5.js"><link rel="prefetch" href="/assets/js/150.c1644898.js"><link rel="prefetch" href="/assets/js/151.b6e4adf0.js"><link rel="prefetch" href="/assets/js/152.7a5071aa.js"><link rel="prefetch" href="/assets/js/153.31abbc68.js"><link rel="prefetch" href="/assets/js/154.9c6bb079.js"><link rel="prefetch" href="/assets/js/155.e5506a40.js"><link rel="prefetch" href="/assets/js/156.992dab50.js"><link rel="prefetch" href="/assets/js/157.12a6ee84.js"><link rel="prefetch" href="/assets/js/158.84dc1045.js"><link rel="prefetch" href="/assets/js/159.fd2022b1.js"><link rel="prefetch" href="/assets/js/16.ff971500.js"><link rel="prefetch" href="/assets/js/160.8d8889cd.js"><link rel="prefetch" href="/assets/js/161.31797def.js"><link rel="prefetch" href="/assets/js/162.bfae7a75.js"><link rel="prefetch" href="/assets/js/163.e5e8094d.js"><link rel="prefetch" href="/assets/js/164.870f212c.js"><link rel="prefetch" href="/assets/js/165.8da20a31.js"><link rel="prefetch" href="/assets/js/166.950a187d.js"><link rel="prefetch" href="/assets/js/167.fd042c52.js"><link rel="prefetch" href="/assets/js/168.7d9f058d.js"><link rel="prefetch" href="/assets/js/169.205df80e.js"><link rel="prefetch" href="/assets/js/17.4b045b26.js"><link rel="prefetch" href="/assets/js/170.270108a3.js"><link rel="prefetch" href="/assets/js/171.9f03d27d.js"><link rel="prefetch" href="/assets/js/172.1882bac7.js"><link rel="prefetch" href="/assets/js/173.fc1fe7d4.js"><link rel="prefetch" href="/assets/js/174.d77c927b.js"><link rel="prefetch" href="/assets/js/175.019b9e83.js"><link rel="prefetch" href="/assets/js/176.5ce31bd8.js"><link rel="prefetch" href="/assets/js/177.9a2006f2.js"><link rel="prefetch" href="/assets/js/178.88265ac1.js"><link rel="prefetch" href="/assets/js/179.3205ce07.js"><link rel="prefetch" href="/assets/js/18.1cdd0514.js"><link rel="prefetch" href="/assets/js/180.2ff51f44.js"><link rel="prefetch" href="/assets/js/181.f792ce97.js"><link rel="prefetch" href="/assets/js/182.103328e1.js"><link rel="prefetch" href="/assets/js/183.40fde303.js"><link rel="prefetch" href="/assets/js/184.1f36ac78.js"><link rel="prefetch" href="/assets/js/185.cf3b1c00.js"><link rel="prefetch" href="/assets/js/186.f7981399.js"><link rel="prefetch" href="/assets/js/187.20fe296d.js"><link rel="prefetch" href="/assets/js/188.7b004fea.js"><link rel="prefetch" href="/assets/js/189.599ca2d9.js"><link rel="prefetch" href="/assets/js/19.5740e4d6.js"><link rel="prefetch" href="/assets/js/190.14087c07.js"><link rel="prefetch" href="/assets/js/191.b0f73f6d.js"><link rel="prefetch" href="/assets/js/192.9d171669.js"><link rel="prefetch" href="/assets/js/193.00975585.js"><link rel="prefetch" href="/assets/js/194.2c7c43b4.js"><link rel="prefetch" href="/assets/js/195.ff126254.js"><link rel="prefetch" href="/assets/js/196.6546ae1c.js"><link rel="prefetch" href="/assets/js/197.eedb67bb.js"><link rel="prefetch" href="/assets/js/198.acdcc22c.js"><link rel="prefetch" href="/assets/js/199.7993a6b2.js"><link rel="prefetch" href="/assets/js/20.8885a4fa.js"><link rel="prefetch" href="/assets/js/200.52f08765.js"><link rel="prefetch" href="/assets/js/201.b85ab10b.js"><link rel="prefetch" href="/assets/js/202.ccec1c85.js"><link rel="prefetch" href="/assets/js/203.3f50a6ed.js"><link rel="prefetch" href="/assets/js/204.f28b803b.js"><link rel="prefetch" href="/assets/js/205.c51c53fd.js"><link rel="prefetch" href="/assets/js/206.ba0aa8f6.js"><link rel="prefetch" href="/assets/js/207.0dce311a.js"><link rel="prefetch" href="/assets/js/208.0ea629a6.js"><link rel="prefetch" href="/assets/js/209.f48c3e71.js"><link rel="prefetch" href="/assets/js/21.0bb62db4.js"><link rel="prefetch" href="/assets/js/210.689db8f2.js"><link rel="prefetch" href="/assets/js/211.72bc0f1f.js"><link rel="prefetch" href="/assets/js/212.456135bd.js"><link rel="prefetch" href="/assets/js/213.d0ea50fc.js"><link rel="prefetch" href="/assets/js/214.1400da91.js"><link rel="prefetch" href="/assets/js/215.aeef3e5d.js"><link rel="prefetch" href="/assets/js/216.a60215af.js"><link rel="prefetch" href="/assets/js/217.d6cd8b69.js"><link rel="prefetch" href="/assets/js/218.1bd500fc.js"><link rel="prefetch" href="/assets/js/219.20faf611.js"><link rel="prefetch" href="/assets/js/22.3e8347a6.js"><link rel="prefetch" href="/assets/js/220.6714179b.js"><link rel="prefetch" href="/assets/js/221.fb5b643d.js"><link rel="prefetch" href="/assets/js/222.d96272e5.js"><link rel="prefetch" href="/assets/js/223.4cb7dc3d.js"><link rel="prefetch" href="/assets/js/224.500d94cc.js"><link rel="prefetch" href="/assets/js/225.d006c34e.js"><link rel="prefetch" href="/assets/js/226.73612150.js"><link rel="prefetch" href="/assets/js/227.b02db9d1.js"><link rel="prefetch" href="/assets/js/228.aad43ff2.js"><link rel="prefetch" href="/assets/js/229.65340802.js"><link rel="prefetch" href="/assets/js/23.ceb5a5ff.js"><link rel="prefetch" href="/assets/js/230.97cab104.js"><link rel="prefetch" href="/assets/js/231.8415349a.js"><link rel="prefetch" href="/assets/js/232.db6d2697.js"><link rel="prefetch" href="/assets/js/233.723d05b1.js"><link rel="prefetch" href="/assets/js/234.26ed5e94.js"><link rel="prefetch" href="/assets/js/235.a586122b.js"><link rel="prefetch" href="/assets/js/236.f802bda8.js"><link rel="prefetch" href="/assets/js/237.cc8767ad.js"><link rel="prefetch" href="/assets/js/238.6485459e.js"><link rel="prefetch" href="/assets/js/239.9fbf3a55.js"><link rel="prefetch" href="/assets/js/24.07267ac6.js"><link rel="prefetch" href="/assets/js/240.1188f244.js"><link rel="prefetch" href="/assets/js/241.cbfb154d.js"><link rel="prefetch" href="/assets/js/242.352cea5a.js"><link rel="prefetch" href="/assets/js/243.07947e1c.js"><link rel="prefetch" href="/assets/js/244.f5b26fa9.js"><link rel="prefetch" href="/assets/js/245.c6030e32.js"><link rel="prefetch" href="/assets/js/246.d3fe99f7.js"><link rel="prefetch" href="/assets/js/247.185ae95d.js"><link rel="prefetch" href="/assets/js/248.a5f1548c.js"><link rel="prefetch" href="/assets/js/249.19691113.js"><link rel="prefetch" href="/assets/js/25.15e1f641.js"><link rel="prefetch" href="/assets/js/250.72fa1d33.js"><link rel="prefetch" href="/assets/js/251.bedd56f7.js"><link rel="prefetch" href="/assets/js/252.32bbcc8b.js"><link rel="prefetch" href="/assets/js/253.b59e5846.js"><link rel="prefetch" href="/assets/js/254.c0e37e1a.js"><link rel="prefetch" href="/assets/js/255.850da3cc.js"><link rel="prefetch" href="/assets/js/256.952d9817.js"><link rel="prefetch" href="/assets/js/257.88667bb8.js"><link rel="prefetch" href="/assets/js/258.c5873c28.js"><link rel="prefetch" href="/assets/js/259.6ae1dcbc.js"><link rel="prefetch" href="/assets/js/26.35b2bbdc.js"><link rel="prefetch" href="/assets/js/260.cb940cc3.js"><link rel="prefetch" href="/assets/js/261.0686c3e9.js"><link rel="prefetch" href="/assets/js/262.d0719839.js"><link rel="prefetch" href="/assets/js/263.ae83ebc1.js"><link rel="prefetch" href="/assets/js/264.209ad8a3.js"><link rel="prefetch" href="/assets/js/265.be8d1ee8.js"><link rel="prefetch" href="/assets/js/266.c51bceaa.js"><link rel="prefetch" href="/assets/js/267.fe8af48a.js"><link rel="prefetch" href="/assets/js/268.e18ed861.js"><link rel="prefetch" href="/assets/js/269.7126031e.js"><link rel="prefetch" href="/assets/js/27.270fcb06.js"><link rel="prefetch" href="/assets/js/270.89ead106.js"><link rel="prefetch" href="/assets/js/271.0643d07a.js"><link rel="prefetch" href="/assets/js/272.32a8e4b0.js"><link rel="prefetch" href="/assets/js/273.9881da47.js"><link rel="prefetch" href="/assets/js/274.2de022ea.js"><link rel="prefetch" href="/assets/js/275.d9a4fe99.js"><link rel="prefetch" href="/assets/js/276.d33de2e9.js"><link rel="prefetch" href="/assets/js/277.1ec367ab.js"><link rel="prefetch" href="/assets/js/278.66ab7c11.js"><link rel="prefetch" href="/assets/js/279.ff51cd15.js"><link rel="prefetch" href="/assets/js/28.ef3f6db2.js"><link rel="prefetch" href="/assets/js/280.ef45ed5f.js"><link rel="prefetch" href="/assets/js/281.87f26b81.js"><link rel="prefetch" href="/assets/js/282.c8ef594b.js"><link rel="prefetch" href="/assets/js/283.15643091.js"><link rel="prefetch" href="/assets/js/284.c4f25b2e.js"><link rel="prefetch" href="/assets/js/285.2d16e4b6.js"><link rel="prefetch" href="/assets/js/286.44eba266.js"><link rel="prefetch" href="/assets/js/287.7613e62f.js"><link rel="prefetch" href="/assets/js/288.b9546d55.js"><link rel="prefetch" href="/assets/js/289.125f85d8.js"><link rel="prefetch" href="/assets/js/29.177558be.js"><link rel="prefetch" href="/assets/js/290.db375797.js"><link rel="prefetch" href="/assets/js/291.90946626.js"><link rel="prefetch" href="/assets/js/292.a4524678.js"><link rel="prefetch" href="/assets/js/293.3ed9b083.js"><link rel="prefetch" href="/assets/js/294.d6c14116.js"><link rel="prefetch" href="/assets/js/295.2b485032.js"><link rel="prefetch" href="/assets/js/296.29d9aa0f.js"><link rel="prefetch" href="/assets/js/297.bfd72435.js"><link rel="prefetch" href="/assets/js/298.fc056989.js"><link rel="prefetch" href="/assets/js/299.0a3f258e.js"><link rel="prefetch" href="/assets/js/3.4dc033c7.js"><link rel="prefetch" href="/assets/js/30.248a229d.js"><link rel="prefetch" href="/assets/js/300.d064d776.js"><link rel="prefetch" href="/assets/js/301.d95b9fc0.js"><link rel="prefetch" href="/assets/js/302.1afa637a.js"><link rel="prefetch" href="/assets/js/303.231beaa8.js"><link rel="prefetch" href="/assets/js/304.05df35d9.js"><link rel="prefetch" href="/assets/js/305.f2d83cf9.js"><link rel="prefetch" href="/assets/js/306.d54f5e5e.js"><link rel="prefetch" href="/assets/js/307.d5df9000.js"><link rel="prefetch" href="/assets/js/308.ef2f52b3.js"><link rel="prefetch" href="/assets/js/309.d1f13319.js"><link rel="prefetch" href="/assets/js/31.32a62b91.js"><link rel="prefetch" href="/assets/js/310.30b84be3.js"><link rel="prefetch" href="/assets/js/311.d79e3145.js"><link rel="prefetch" href="/assets/js/312.58e24f19.js"><link rel="prefetch" href="/assets/js/313.9809a122.js"><link rel="prefetch" href="/assets/js/314.e92e41b1.js"><link rel="prefetch" href="/assets/js/315.8c2fc1d4.js"><link rel="prefetch" href="/assets/js/316.a0904343.js"><link rel="prefetch" href="/assets/js/317.18e42b54.js"><link rel="prefetch" href="/assets/js/318.6b051d27.js"><link rel="prefetch" href="/assets/js/319.513b0197.js"><link rel="prefetch" href="/assets/js/32.23b75afc.js"><link rel="prefetch" href="/assets/js/320.ae91bc36.js"><link rel="prefetch" href="/assets/js/321.a194efea.js"><link rel="prefetch" href="/assets/js/322.8273020e.js"><link rel="prefetch" href="/assets/js/323.d332e08f.js"><link rel="prefetch" href="/assets/js/324.ed867b64.js"><link rel="prefetch" href="/assets/js/325.c43ccf9a.js"><link rel="prefetch" href="/assets/js/326.9497d879.js"><link rel="prefetch" href="/assets/js/327.7f095e40.js"><link rel="prefetch" href="/assets/js/328.cb5c5847.js"><link rel="prefetch" href="/assets/js/329.d36b59f3.js"><link rel="prefetch" href="/assets/js/33.b258b779.js"><link rel="prefetch" href="/assets/js/330.b1a092b9.js"><link rel="prefetch" href="/assets/js/331.ff86566f.js"><link rel="prefetch" href="/assets/js/332.0daafa9f.js"><link rel="prefetch" href="/assets/js/333.b7e50524.js"><link rel="prefetch" href="/assets/js/334.7dcf9f0b.js"><link rel="prefetch" href="/assets/js/335.862c410a.js"><link rel="prefetch" href="/assets/js/336.c6384990.js"><link rel="prefetch" href="/assets/js/337.ab9d5e52.js"><link rel="prefetch" href="/assets/js/338.598ae59e.js"><link rel="prefetch" href="/assets/js/339.9b810ff8.js"><link rel="prefetch" href="/assets/js/34.3bd60f1f.js"><link rel="prefetch" href="/assets/js/340.5609a53c.js"><link rel="prefetch" href="/assets/js/341.969d335a.js"><link rel="prefetch" href="/assets/js/342.43f85dba.js"><link rel="prefetch" href="/assets/js/343.47280ef9.js"><link rel="prefetch" href="/assets/js/344.346ae5fc.js"><link rel="prefetch" href="/assets/js/345.32166361.js"><link rel="prefetch" href="/assets/js/346.86ff128b.js"><link rel="prefetch" href="/assets/js/347.3c5421fe.js"><link rel="prefetch" href="/assets/js/348.95910300.js"><link rel="prefetch" href="/assets/js/349.004158d8.js"><link rel="prefetch" href="/assets/js/35.18e8f66a.js"><link rel="prefetch" href="/assets/js/350.e10b195b.js"><link rel="prefetch" href="/assets/js/351.12f89875.js"><link rel="prefetch" href="/assets/js/352.83957394.js"><link rel="prefetch" href="/assets/js/353.475971b9.js"><link rel="prefetch" href="/assets/js/354.8af7b26b.js"><link rel="prefetch" href="/assets/js/355.85925e24.js"><link rel="prefetch" href="/assets/js/356.1d77cf9c.js"><link rel="prefetch" href="/assets/js/357.b467d481.js"><link rel="prefetch" href="/assets/js/358.ac96f32d.js"><link rel="prefetch" href="/assets/js/359.e048bd10.js"><link rel="prefetch" href="/assets/js/36.a7ae257c.js"><link rel="prefetch" href="/assets/js/360.e9b45545.js"><link rel="prefetch" href="/assets/js/361.ad1bb45b.js"><link rel="prefetch" href="/assets/js/362.4e24a30b.js"><link rel="prefetch" href="/assets/js/363.64f7dad5.js"><link rel="prefetch" href="/assets/js/364.d9e3ebc2.js"><link rel="prefetch" href="/assets/js/365.330e3086.js"><link rel="prefetch" href="/assets/js/366.a6c0afb3.js"><link rel="prefetch" href="/assets/js/367.ffcefa40.js"><link rel="prefetch" href="/assets/js/368.664c8e4d.js"><link rel="prefetch" href="/assets/js/369.67da2dd2.js"><link rel="prefetch" href="/assets/js/37.d709f9a9.js"><link rel="prefetch" href="/assets/js/370.b1791970.js"><link rel="prefetch" href="/assets/js/371.743a461a.js"><link rel="prefetch" href="/assets/js/372.7703ef1b.js"><link rel="prefetch" href="/assets/js/373.17de31f6.js"><link rel="prefetch" href="/assets/js/374.e508be9e.js"><link rel="prefetch" href="/assets/js/375.6e2e9fe3.js"><link rel="prefetch" href="/assets/js/376.8ca3511a.js"><link rel="prefetch" href="/assets/js/377.bfcee39c.js"><link rel="prefetch" href="/assets/js/378.21852b78.js"><link rel="prefetch" href="/assets/js/379.9649c307.js"><link rel="prefetch" href="/assets/js/38.68ca920e.js"><link rel="prefetch" href="/assets/js/380.dadb4418.js"><link rel="prefetch" href="/assets/js/381.a03c993d.js"><link rel="prefetch" href="/assets/js/382.bb7c22c1.js"><link rel="prefetch" href="/assets/js/383.bd68b2e5.js"><link rel="prefetch" href="/assets/js/384.f6dc7457.js"><link rel="prefetch" href="/assets/js/385.14287a91.js"><link rel="prefetch" href="/assets/js/386.a6284ac2.js"><link rel="prefetch" href="/assets/js/387.c51f147a.js"><link rel="prefetch" href="/assets/js/388.06d9651e.js"><link rel="prefetch" href="/assets/js/389.2d85d927.js"><link rel="prefetch" href="/assets/js/39.de850db9.js"><link rel="prefetch" href="/assets/js/390.2397b3e0.js"><link rel="prefetch" href="/assets/js/391.8413aaaf.js"><link rel="prefetch" href="/assets/js/392.00eecaa4.js"><link rel="prefetch" href="/assets/js/393.351dd3fd.js"><link rel="prefetch" href="/assets/js/394.d2cc4a70.js"><link rel="prefetch" href="/assets/js/395.abee64bf.js"><link rel="prefetch" href="/assets/js/396.bc265e6a.js"><link rel="prefetch" href="/assets/js/397.e4a96944.js"><link rel="prefetch" href="/assets/js/398.f0d26b29.js"><link rel="prefetch" href="/assets/js/399.e77c3ddc.js"><link rel="prefetch" href="/assets/js/4.2f502b73.js"><link rel="prefetch" href="/assets/js/40.91635261.js"><link rel="prefetch" href="/assets/js/400.c7eac401.js"><link rel="prefetch" href="/assets/js/401.06092d16.js"><link rel="prefetch" href="/assets/js/402.4e08e496.js"><link rel="prefetch" href="/assets/js/403.deba4c77.js"><link rel="prefetch" href="/assets/js/404.9100e4df.js"><link rel="prefetch" href="/assets/js/405.e9f451e2.js"><link rel="prefetch" href="/assets/js/406.a0da4aa4.js"><link rel="prefetch" href="/assets/js/407.58b2b123.js"><link rel="prefetch" href="/assets/js/408.eb2cde2d.js"><link rel="prefetch" href="/assets/js/409.9041f749.js"><link rel="prefetch" href="/assets/js/41.169a3cfc.js"><link rel="prefetch" href="/assets/js/410.c12f3710.js"><link rel="prefetch" href="/assets/js/411.452cda45.js"><link rel="prefetch" href="/assets/js/412.8691317a.js"><link rel="prefetch" href="/assets/js/413.25a68f21.js"><link rel="prefetch" href="/assets/js/414.7a9699d8.js"><link rel="prefetch" href="/assets/js/415.6e8cdaff.js"><link rel="prefetch" href="/assets/js/416.e8a86529.js"><link rel="prefetch" href="/assets/js/417.28330bde.js"><link rel="prefetch" href="/assets/js/418.2fed8e17.js"><link rel="prefetch" href="/assets/js/419.773032f4.js"><link rel="prefetch" href="/assets/js/42.e17cd061.js"><link rel="prefetch" href="/assets/js/420.56681228.js"><link rel="prefetch" href="/assets/js/421.0d170e13.js"><link rel="prefetch" href="/assets/js/422.fc8c2a11.js"><link rel="prefetch" href="/assets/js/423.e34ea6a6.js"><link rel="prefetch" href="/assets/js/424.20fe8748.js"><link rel="prefetch" href="/assets/js/425.f001b7cd.js"><link rel="prefetch" href="/assets/js/426.a91230ef.js"><link rel="prefetch" href="/assets/js/427.c6c9fe8d.js"><link rel="prefetch" href="/assets/js/428.9d4f5e41.js"><link rel="prefetch" href="/assets/js/429.c81cfd3f.js"><link rel="prefetch" href="/assets/js/43.f88aa667.js"><link rel="prefetch" href="/assets/js/430.eb61372f.js"><link rel="prefetch" href="/assets/js/431.4a25365a.js"><link rel="prefetch" href="/assets/js/432.42fc1bbe.js"><link rel="prefetch" href="/assets/js/433.8e9ac9f0.js"><link rel="prefetch" href="/assets/js/434.fdd0b160.js"><link rel="prefetch" href="/assets/js/435.d6cbac31.js"><link rel="prefetch" href="/assets/js/436.17750ba2.js"><link rel="prefetch" href="/assets/js/437.a1468099.js"><link rel="prefetch" href="/assets/js/438.ac6a45c2.js"><link rel="prefetch" href="/assets/js/439.242ae27a.js"><link rel="prefetch" href="/assets/js/44.3f36e228.js"><link rel="prefetch" href="/assets/js/440.b80b520c.js"><link rel="prefetch" href="/assets/js/441.e51a4bbb.js"><link rel="prefetch" href="/assets/js/442.ab0b3eda.js"><link rel="prefetch" href="/assets/js/443.47e44250.js"><link rel="prefetch" href="/assets/js/444.54c3b425.js"><link rel="prefetch" href="/assets/js/445.e006c12e.js"><link rel="prefetch" href="/assets/js/446.f2b79730.js"><link rel="prefetch" href="/assets/js/447.2509a397.js"><link rel="prefetch" href="/assets/js/448.b7d69dfc.js"><link rel="prefetch" href="/assets/js/449.8a575d9b.js"><link rel="prefetch" href="/assets/js/45.be1455b1.js"><link rel="prefetch" href="/assets/js/450.ad421aeb.js"><link rel="prefetch" href="/assets/js/451.f09912ae.js"><link rel="prefetch" href="/assets/js/452.f528c4ce.js"><link rel="prefetch" href="/assets/js/453.fc4dc0f9.js"><link rel="prefetch" href="/assets/js/454.733c315c.js"><link rel="prefetch" href="/assets/js/455.44fe35b1.js"><link rel="prefetch" href="/assets/js/456.7f650035.js"><link rel="prefetch" href="/assets/js/457.77ebc183.js"><link rel="prefetch" href="/assets/js/458.133078f4.js"><link rel="prefetch" href="/assets/js/459.33728b5e.js"><link rel="prefetch" href="/assets/js/46.5ad74cb5.js"><link rel="prefetch" href="/assets/js/460.6946f564.js"><link rel="prefetch" href="/assets/js/461.6f906c5c.js"><link rel="prefetch" href="/assets/js/462.26eca27e.js"><link rel="prefetch" href="/assets/js/463.e02e5f65.js"><link rel="prefetch" href="/assets/js/464.0df25c63.js"><link rel="prefetch" href="/assets/js/465.80cd249e.js"><link rel="prefetch" href="/assets/js/466.a0921e86.js"><link rel="prefetch" href="/assets/js/467.1bec2b47.js"><link rel="prefetch" href="/assets/js/468.b6fa37c1.js"><link rel="prefetch" href="/assets/js/469.a1d2487e.js"><link rel="prefetch" href="/assets/js/47.03df5d0a.js"><link rel="prefetch" href="/assets/js/470.36a4b9c8.js"><link rel="prefetch" href="/assets/js/471.28895273.js"><link rel="prefetch" href="/assets/js/472.b9fe392f.js"><link rel="prefetch" href="/assets/js/473.a2835d9c.js"><link rel="prefetch" href="/assets/js/474.cd57b37d.js"><link rel="prefetch" href="/assets/js/475.8d5a9792.js"><link rel="prefetch" href="/assets/js/476.f7f3781c.js"><link rel="prefetch" href="/assets/js/477.9b710b04.js"><link rel="prefetch" href="/assets/js/478.46d66687.js"><link rel="prefetch" href="/assets/js/479.e7444705.js"><link rel="prefetch" href="/assets/js/48.48f286ba.js"><link rel="prefetch" href="/assets/js/480.da2b750a.js"><link rel="prefetch" href="/assets/js/481.838a8e04.js"><link rel="prefetch" href="/assets/js/482.02bf8041.js"><link rel="prefetch" href="/assets/js/483.bb29ef2f.js"><link rel="prefetch" href="/assets/js/484.aebb68bd.js"><link rel="prefetch" href="/assets/js/485.a66ab41b.js"><link rel="prefetch" href="/assets/js/486.8df516fb.js"><link rel="prefetch" href="/assets/js/487.86d28130.js"><link rel="prefetch" href="/assets/js/488.0863da70.js"><link rel="prefetch" href="/assets/js/489.5d4d9e88.js"><link rel="prefetch" href="/assets/js/49.1ca52d11.js"><link rel="prefetch" href="/assets/js/490.3fd65d85.js"><link rel="prefetch" href="/assets/js/491.ae3a2f87.js"><link rel="prefetch" href="/assets/js/492.6f54679b.js"><link rel="prefetch" href="/assets/js/493.a594b1f4.js"><link rel="prefetch" href="/assets/js/494.4f03fae1.js"><link rel="prefetch" href="/assets/js/495.ae50b66b.js"><link rel="prefetch" href="/assets/js/496.bc92d835.js"><link rel="prefetch" href="/assets/js/497.eac65251.js"><link rel="prefetch" href="/assets/js/498.1d726726.js"><link rel="prefetch" href="/assets/js/499.01e4f7d1.js"><link rel="prefetch" href="/assets/js/5.7b1f056c.js"><link rel="prefetch" href="/assets/js/50.73732fe2.js"><link rel="prefetch" href="/assets/js/500.062ade4e.js"><link rel="prefetch" href="/assets/js/501.e6a711dc.js"><link rel="prefetch" href="/assets/js/502.c79e28e2.js"><link rel="prefetch" href="/assets/js/503.bf97bce9.js"><link rel="prefetch" href="/assets/js/504.b2784ef0.js"><link rel="prefetch" href="/assets/js/505.0767ba54.js"><link rel="prefetch" href="/assets/js/506.8ae14637.js"><link rel="prefetch" href="/assets/js/507.003e2349.js"><link rel="prefetch" href="/assets/js/508.ecb5e6bd.js"><link rel="prefetch" href="/assets/js/509.b644f44a.js"><link rel="prefetch" href="/assets/js/51.bed7cf61.js"><link rel="prefetch" href="/assets/js/510.b660946f.js"><link rel="prefetch" href="/assets/js/511.cb186c8f.js"><link rel="prefetch" href="/assets/js/512.d176170f.js"><link rel="prefetch" href="/assets/js/513.18a1afa5.js"><link rel="prefetch" href="/assets/js/514.4a33e931.js"><link rel="prefetch" href="/assets/js/515.8496bd4d.js"><link rel="prefetch" href="/assets/js/516.529b9476.js"><link rel="prefetch" href="/assets/js/517.289738f1.js"><link rel="prefetch" href="/assets/js/518.eda39556.js"><link rel="prefetch" href="/assets/js/519.85b42de5.js"><link rel="prefetch" href="/assets/js/52.22a7da62.js"><link rel="prefetch" href="/assets/js/520.8b531775.js"><link rel="prefetch" href="/assets/js/521.8e4b66fb.js"><link rel="prefetch" href="/assets/js/522.050bfc55.js"><link rel="prefetch" href="/assets/js/523.e973f1e0.js"><link rel="prefetch" href="/assets/js/524.fd160738.js"><link rel="prefetch" href="/assets/js/525.5a4bc307.js"><link rel="prefetch" href="/assets/js/526.361bc4fa.js"><link rel="prefetch" href="/assets/js/527.06ee9d73.js"><link rel="prefetch" href="/assets/js/528.22d9b118.js"><link rel="prefetch" href="/assets/js/529.b7592c16.js"><link rel="prefetch" href="/assets/js/53.767f3bbf.js"><link rel="prefetch" href="/assets/js/530.ad96d564.js"><link rel="prefetch" href="/assets/js/531.61905243.js"><link rel="prefetch" href="/assets/js/532.11aebf61.js"><link rel="prefetch" href="/assets/js/533.0cddb226.js"><link rel="prefetch" href="/assets/js/534.d1c9cc36.js"><link rel="prefetch" href="/assets/js/54.d1f9eec8.js"><link rel="prefetch" href="/assets/js/55.aa90e812.js"><link rel="prefetch" href="/assets/js/56.4d7f81f2.js"><link rel="prefetch" href="/assets/js/57.3c540e8d.js"><link rel="prefetch" href="/assets/js/58.fc1c23f2.js"><link rel="prefetch" href="/assets/js/59.c7a7a4c0.js"><link rel="prefetch" href="/assets/js/6.056886f1.js"><link rel="prefetch" href="/assets/js/60.40e590ae.js"><link rel="prefetch" href="/assets/js/61.e400b323.js"><link rel="prefetch" href="/assets/js/62.bbb30ef1.js"><link rel="prefetch" href="/assets/js/63.c31cd2bb.js"><link rel="prefetch" href="/assets/js/64.62321188.js"><link rel="prefetch" href="/assets/js/65.44adc2b3.js"><link rel="prefetch" href="/assets/js/66.b5740d4f.js"><link rel="prefetch" href="/assets/js/67.76c3624c.js"><link rel="prefetch" href="/assets/js/68.6dd982d4.js"><link rel="prefetch" href="/assets/js/69.eb1390ec.js"><link rel="prefetch" href="/assets/js/7.a96cecf9.js"><link rel="prefetch" href="/assets/js/70.8c882bdd.js"><link rel="prefetch" href="/assets/js/71.001c968a.js"><link rel="prefetch" href="/assets/js/72.ae4ca075.js"><link rel="prefetch" href="/assets/js/74.fba60e37.js"><link rel="prefetch" href="/assets/js/75.0b1bf7bf.js"><link rel="prefetch" href="/assets/js/76.ee19d90e.js"><link rel="prefetch" href="/assets/js/77.fe8ac5f0.js"><link rel="prefetch" href="/assets/js/78.ae09432e.js"><link rel="prefetch" href="/assets/js/79.c1b10e2d.js"><link rel="prefetch" href="/assets/js/80.ae809538.js"><link rel="prefetch" href="/assets/js/81.ca11b4c7.js"><link rel="prefetch" href="/assets/js/82.308b98e4.js"><link rel="prefetch" href="/assets/js/83.355d9dde.js"><link rel="prefetch" href="/assets/js/84.a2b61d48.js"><link rel="prefetch" href="/assets/js/85.cf992fda.js"><link rel="prefetch" href="/assets/js/86.f547c7ea.js"><link rel="prefetch" href="/assets/js/87.2b02746a.js"><link rel="prefetch" href="/assets/js/88.e939aad5.js"><link rel="prefetch" href="/assets/js/89.b000c42a.js"><link rel="prefetch" href="/assets/js/90.dadef001.js"><link rel="prefetch" href="/assets/js/91.c0fe2e32.js"><link rel="prefetch" href="/assets/js/92.e09650b9.js"><link rel="prefetch" href="/assets/js/93.427c7280.js"><link rel="prefetch" href="/assets/js/94.90fc5f58.js"><link rel="prefetch" href="/assets/js/95.13d42b3a.js"><link rel="prefetch" href="/assets/js/96.d0a2ea8c.js"><link rel="prefetch" href="/assets/js/97.cc9a0eda.js"><link rel="prefetch" href="/assets/js/98.3c9f1b7e.js"><link rel="prefetch" href="/assets/js/99.0420dcbc.js"><link rel="prefetch" href="/assets/js/vendors~docsearch.33b2b47d.js">
    <link rel="stylesheet" href="/assets/css/0.styles.40c15e22.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><img src="/logo.png" alt="鱼皮的编程宝典" class="logo"> <span class="site-name can-hide">鱼皮的编程宝典</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/学习路线/" class="nav-link">
  学习路线
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="自学之路" class="dropdown-title"><span class="title">自学之路</span> <span class="arrow down"></span></button> <button type="button" aria-label="自学之路" class="mobile-dropdown-title"><span class="title">自学之路</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/自学之路/#大学经历" class="nav-link">
  大学经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#求职经历" class="nav-link">
  求职经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#职场工作" class="nav-link">
  职场工作
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#创作经历" class="nav-link">
  创作经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#创业经历" class="nav-link">
  创业经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#生活日常" class="nav-link">
  生活日常
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="编程分享" class="dropdown-title"><span class="title">编程分享</span> <span class="arrow down"></span></button> <button type="button" aria-label="编程分享" class="mobile-dropdown-title"><span class="title">编程分享</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/编程分享/#入门必看-学习路线" class="nav-link">
  入门必看-学习路线
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#学习指南" class="nav-link">
  学习指南
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#开发经验" class="nav-link">
  开发经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#求职经验" class="nav-link">
  求职经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#职场经验" class="nav-link">
  职场经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#技术分享" class="nav-link">
  技术分享
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#实战教程" class="nav-link">
  实战教程
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#其他" class="nav-link">
  其他
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#📚-项目教程" class="nav-link">
  项目教程
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#个人作品" class="nav-link">
  个人作品
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#🎁-编程资源" class="nav-link">
  编程资源
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#🌐-科技科普" class="nav-link">
  科技科普
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="项目实战" class="dropdown-title"><span class="title">项目实战</span> <span class="arrow down"></span></button> <button type="button" aria-label="项目实战" class="mobile-dropdown-title"><span class="title">项目实战</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/项目实战/OJ 判题系统.html" class="nav-link">
  OJ 判题系统
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/智能 BI 平台.html" class="nav-link">
  智能 BI 平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/聚合搜索平台.html" class="nav-link">
  聚合搜索平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/API 开放平台.html" class="nav-link">
  API 开放平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/伙伴匹配系统.html" class="nav-link">
  伙伴匹配系统
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/用户中心项目.html" class="nav-link">
  用户中心项目
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/Java 后端万用项目模板.html" class="nav-link">
  Java 后端万用项目模板
</a></li></ul></div></div><div class="nav-item"><a href="/知识碎片/" class="nav-link">
  知识碎片
</a></div><div class="nav-item"><a href="/编程导航/" class="nav-link">
  🔥 编程导航
</a></div><div class="nav-item"><a href="/产品服务/" class="nav-link">
  产品服务
</a></div><div class="nav-item"><a href="/作者/" class="nav-link">
  作者
</a></div> <a href="https://github.com/liyupi/codefather" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/学习路线/" class="nav-link">
  学习路线
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="自学之路" class="dropdown-title"><span class="title">自学之路</span> <span class="arrow down"></span></button> <button type="button" aria-label="自学之路" class="mobile-dropdown-title"><span class="title">自学之路</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/自学之路/#大学经历" class="nav-link">
  大学经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#求职经历" class="nav-link">
  求职经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#职场工作" class="nav-link">
  职场工作
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#创作经历" class="nav-link">
  创作经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#创业经历" class="nav-link">
  创业经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#生活日常" class="nav-link">
  生活日常
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="编程分享" class="dropdown-title"><span class="title">编程分享</span> <span class="arrow down"></span></button> <button type="button" aria-label="编程分享" class="mobile-dropdown-title"><span class="title">编程分享</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/编程分享/#入门必看-学习路线" class="nav-link">
  入门必看-学习路线
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#学习指南" class="nav-link">
  学习指南
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#开发经验" class="nav-link">
  开发经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#求职经验" class="nav-link">
  求职经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#职场经验" class="nav-link">
  职场经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#技术分享" class="nav-link">
  技术分享
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#实战教程" class="nav-link">
  实战教程
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#其他" class="nav-link">
  其他
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#📚-项目教程" class="nav-link">
  项目教程
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#个人作品" class="nav-link">
  个人作品
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#🎁-编程资源" class="nav-link">
  编程资源
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#🌐-科技科普" class="nav-link">
  科技科普
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="项目实战" class="dropdown-title"><span class="title">项目实战</span> <span class="arrow down"></span></button> <button type="button" aria-label="项目实战" class="mobile-dropdown-title"><span class="title">项目实战</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/项目实战/OJ 判题系统.html" class="nav-link">
  OJ 判题系统
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/智能 BI 平台.html" class="nav-link">
  智能 BI 平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/聚合搜索平台.html" class="nav-link">
  聚合搜索平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/API 开放平台.html" class="nav-link">
  API 开放平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/伙伴匹配系统.html" class="nav-link">
  伙伴匹配系统
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/用户中心项目.html" class="nav-link">
  用户中心项目
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/Java 后端万用项目模板.html" class="nav-link">
  Java 后端万用项目模板
</a></li></ul></div></div><div class="nav-item"><a href="/知识碎片/" class="nav-link">
  知识碎片
</a></div><div class="nav-item"><a href="/编程导航/" class="nav-link">
  🔥 编程导航
</a></div><div class="nav-item"><a href="/产品服务/" class="nav-link">
  产品服务
</a></div><div class="nav-item"><a href="/作者/" class="nav-link">
  作者
</a></div> <a href="https://github.com/liyupi/codefather" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav>  <ul class="sidebar-links"><li><a href="/%E7%9F%A5%E8%AF%86%E7%A2%8E%E7%89%87/" class="sidebar-link">编程导航知识碎片</a></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading open"><span>知识碎片</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/_4-%E7%A7%8D%E6%96%B9%E6%B3%95-%E5%BF%AB%E9%80%9F%E5%88%9D%E5%A7%8B%E5%8C%96-java-%E9%A1%B9%E7%9B%AE/" class="sidebar-link">4 种方法，快速初始化 Java 项目</a></li><li><a href="/%E4%BD%BF%E7%94%A8%E5%90%8E%E7%AB%AF%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%99%A8-%E6%8F%90%E9%AB%98%E5%BC%80%E5%8F%91%E6%95%88%E7%8E%87/" class="sidebar-link">使用后端代码生成器，提高开发效率</a></li><li><a href="/%E5%89%8D%E7%AB%AF%E5%BF%85%E5%AD%A6%E7%9A%84%E5%BC%80%E5%8F%91%E6%A1%86%E6%9E%B6-ant-design-pro/" class="sidebar-link">前端必学的开发框架，Ant Design Pro</a></li><li><a href="/%E5%90%8E%E7%AB%AF%E5%A6%82%E4%BD%95%E7%BC%96%E5%86%99%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/" class="sidebar-link">后端如何编写单元测试？</a></li><li><a href="/%E5%90%8E%E7%AB%AF%E9%A1%B9%E7%9B%AE%E7%BB%8F%E5%85%B8%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E4%BB%8B%E7%BB%8D/" class="sidebar-link">后端项目经典分层架构介绍</a></li><li><a href="/%E5%A6%82%E4%BD%95%E9%AB%98%E6%95%88%E6%B5%8B%E8%AF%95%E6%8E%A5%E5%8F%A3-%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3/" class="sidebar-link">如何高效测试接口？自动生成接口文档</a></li><li><a href="/%E5%BC%80%E5%8F%91%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1%E7%BE%A4%E6%9C%BA%E5%99%A8%E4%BA%BA-%E5%AE%9E%E7%8E%B0%E5%AE%9A%E6%97%B6%E6%8F%90%E9%86%92/" class="sidebar-link">开发企业微信群机器人，实现定时提醒</a></li><li><a href="/%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3%E6%95%B0%E6%8D%AE%E6%9F%A5%E8%AF%A2%E4%B8%BA%E7%A9%BA%E7%9A%84-bug/" class="sidebar-link">如何解决数据查询为空的 Bug？</a></li><li><a href="/git-%E6%8F%90%E4%BA%A4%E7%AB%9F%E7%84%B6%E8%BF%98%E8%83%BD%E8%BF%99%E4%B9%88%E7%94%A8/" class="sidebar-link">Git 提交竟然还能这么用？</a></li><li><a href="/%E3%80%90%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%91%E8%A3%85%E9%A5%B0%E8%80%85%E6%A8%A1%E5%BC%8F-%E5%8F%8A%E5%85%B6%E5%9C%A8jdk%E6%BA%90%E7%A0%81%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/" class="sidebar-link">【设计模式】装饰者模式，及其在JDK源码中的应用</a></li><li><a href="/%E7%94%A8%E5%9B%9B%E7%A7%8D%E9%94%81%E5%AE%9E%E7%8E%B0%E5%8A%A0%E5%85%A5%E9%98%9F%E4%BC%8D%E5%8A%9F%E8%83%BD-%E4%BC%99%E4%BC%B4%E5%8C%B9%E9%85%8D%E7%B3%BB%E7%BB%9F/" class="sidebar-link">用四种锁实现加入队伍功能(伙伴匹配系统)</a></li><li><a href="/%E7%94%A8-validated%E6%B3%A8%E8%A7%A3%E5%AE%9E%E7%8E%B0%E9%9D%9E%E7%A9%BA%E6%A0%A1%E9%AA%8C/" class="sidebar-link">用@Validated注解实现非空校验</a></li><li><a href="/%E4%BD%BF%E7%94%A8%E7%AC%AC%E4%B8%89%E6%96%B9%E6%9C%8D%E5%8A%A1-%E5%AE%9D%E5%A1%94-%E5%BF%AB%E9%80%9F%E9%83%A8%E7%BD%B2%E9%A1%B9%E7%9B%AE/" class="sidebar-link">使用第三方服务（宝塔）快速部署项目</a></li><li><a href="/restful-%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E4%B8%8E%E6%B5%8B%E8%AF%95-spring-boot/" class="sidebar-link">RESTful 接口实现与测试( Spring Boot )</a></li><li><a href="/%E5%BF%AB%E9%80%9F%E5%AD%A6%E4%BC%9A%E4%B8%BA%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE%E5%81%9A%E8%B4%A1%E7%8C%AE/" class="sidebar-link">快速学会为开源项目做贡献</a></li><li><a href="/%E6%95%B4%E5%90%88spring-jdbc%E6%93%8D%E4%BD%9C%E6%95%B0%E6%8D%AE%E5%BA%93/" class="sidebar-link">整合Spring JDBC操作数据库</a></li><li><a href="/maven-%E4%B8%AD%E5%A4%AE%E4%BB%93%E5%BA%93%E5%8F%91%E5%8C%85%E6%B5%81%E7%A8%8B/" class="sidebar-link">Maven 中央仓库发包流程</a></li><li><a href="/nacos-%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83%E6%90%AD%E5%BB%BA/" class="sidebar-link">Nacos 配置中心搭建</a></li><li><a href="/bean-%E6%8B%B7%E8%B4%9D%E4%B9%8B-mapstruct/" class="sidebar-link">Bean 拷贝之 MapStruct</a></li><li><a href="/%E4%BD%BF%E7%94%A8-freemarker-%E6%A8%A1%E6%9D%BF%E5%BC%95%E6%93%8E%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81/" class="sidebar-link">使用 FreeMarker 模板引擎生成代码</a></li><li><a href="/%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E5%BF%BD%E7%95%A5-git-%E6%96%87%E4%BB%B6%E6%8F%90%E4%BA%A4/" class="sidebar-link">如何快速忽略 Git 文件提交？</a></li><li><a href="/easyexcel-%E5%AF%BC%E5%87%BA%E6%96%87%E4%BB%B6/" class="sidebar-link">EasyExcel 导出文件</a></li><li><a href="/%E5%A6%82%E4%BD%95%E5%A4%84%E7%90%86-completablefuture-%E4%B8%AD%E7%9A%84%E4%B8%A4%E7%A7%8D%E5%BC%82%E5%B8%B8-%E6%99%BA%E8%83%BDbi%E9%A1%B9%E7%9B%AE/" class="sidebar-link">如何处理 CompletableFuture 中的两种异常(智能BI项目)</a></li><li><a href="/%E5%9F%BA%E4%BA%8E-session-%E5%AE%9E%E7%8E%B0%E7%9F%AD%E4%BF%A1%E7%99%BB%E5%BD%95/" class="sidebar-link">基于 Session 实现短信登录</a></li><li><a href="/synchronized-%E5%85%B3%E9%94%AE%E5%AD%97%E8%AF%A6%E8%A7%A3/" class="sidebar-link">Synchronized 关键字详解</a></li><li><a href="/redission-%E8%A7%A3%E9%94%81%E5%BC%82%E5%B8%B8%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/" class="sidebar-link">Redission 解锁异常解决方案</a></li><li><a href="/%E5%AE%9E%E7%8E%B0%E4%B8%BB%E9%A2%98%E6%8D%A2%E8%82%A4-css-%E5%8F%98%E9%87%8F-%E7%B1%BB%E5%90%8D%E5%88%87%E6%8D%A2/" class="sidebar-link">实现主题换肤( CSS 变量 + 类名切换)</a></li><li><a href="/springboot-%E6%95%B4%E5%90%88-minio%E5%85%A8%E6%B5%81%E7%A8%8B-%E4%BB%8E%E5%AE%89%E8%A3%85-minio-%E5%88%B0%E5%BA%94%E7%94%A8/" class="sidebar-link">SpringBoot 整合 Minio全流程（从安装 Minio 到应用）</a></li><li><a href="/%E3%80%90redis%E5%BA%94%E7%94%A8%E3%80%91uv%E7%BB%9F%E8%AE%A1/" class="sidebar-link">【Redis应用】UV统计</a></li><li><a href="/%E3%80%8A%E8%B4%AF%E7%A9%BF%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%8B%E7%AC%AC%E4%B8%89%E6%96%B9%E7%99%BB%E5%BD%95%E5%AE%9E%E8%B7%B5-%E9%99%84%E5%89%8D%E5%90%8E%E7%AB%AF%E5%AE%8C%E6%95%B4%E8%BF%87%E7%A8%8B%E5%8F%8A%E6%B5%8B%E8%AF%95/" class="sidebar-link">《贯穿设计模式》第三方登录实践[附前后端完整过程及测试]</a></li><li><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/" aria-current="page" class="active sidebar-link">Websocket.+ Spring-SseEmitter3 实现讯飞星火Java客户端</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#websocket" class="sidebar-link">websocket</a></li><li class="sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#讯飞星火" class="sidebar-link">讯飞星火</a></li><li class="sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#http流式传输-sseemitter" class="sidebar-link">HTTP流式传输-SseEmitter</a></li></ul></li><li><a href="/%E7%94%A8%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E5%AE%9E%E7%8E%B0%E6%A3%80%E6%9F%A5%E7%94%A8%E6%88%B7%E5%90%8D%E6%98%AF%E5%90%A6%E5%AD%98%E5%9C%A8-%E7%94%A8%E6%88%B7%E6%B3%A8%E5%86%8C/" class="sidebar-link">用布隆过滤器实现检查用户名是否存在(用户注册)</a></li><li><a href="/springsecurity-%E6%95%B4%E5%90%88-oauth2/" class="sidebar-link">SpringSecurity 整合 Oauth2</a></li><li><a href="/%E5%89%8D%E7%AB%AF%E5%88%9D%E5%A7%8B%E5%8C%96-ant-design-pro-%E7%AC%94%E8%AE%B0/" class="sidebar-link">前端初始化 Ant Design Pro 笔记</a></li><li><a href="/liteflow-%E7%BC%96%E6%8E%92%E5%BC%8F%E7%BC%96%E7%A8%8B%E7%AE%80%E5%8D%95%E4%BB%8B%E7%BB%8D/" class="sidebar-link">LiteFlow 编排式编程简单介绍</a></li><li><a href="/%E5%9F%BA%E4%BA%8E-redis-%E5%AE%9E%E7%8E%B0%E7%9F%AD%E4%BF%A1%E7%99%BB%E5%BD%95/" class="sidebar-link">基于 Redis 实现短信登录</a></li><li><a href="/mysql-%E4%B8%80%E6%A3%B5-b-%E6%A0%91%E5%8F%AF%E4%BB%A5%E5%AD%98%E5%A4%9A%E5%B0%91%E6%9D%A1%E6%95%B0%E6%8D%AE/" class="sidebar-link">MySQL 一棵 B + 树可以存多少条数据？</a></li><li><a href="/springboot-%E6%95%B4%E5%90%88-elk-%E5%AE%9E%E7%8E%B0%E6%97%A5%E5%BF%97%E9%87%87%E9%9B%86%E4%B8%8E%E7%9B%91%E6%8E%A7/" class="sidebar-link">SpringBoot 整合 ELK 实现日志采集与监控</a></li><li><a href="/%E5%9F%BA%E4%BA%8E%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%A8%E8%A7%A3%E7%9A%84-redisson-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%AE%9E%E7%8E%B0/" class="sidebar-link">基于自定义注解的 Redisson 分布式锁实现</a></li><li><a href="/%E7%8E%A9%E8%BD%AC%E5%BC%82%E6%AD%A5%E7%BC%96%E7%A8%8B%E5%88%A9%E5%99%A8-completablefuture/" class="sidebar-link">玩转异步编程利器 CompletableFuture</a></li><li><a href="/%E9%80%9A%E8%BF%87-windows-%E8%84%9A%E6%9C%AC%E6%89%A7%E8%A1%8C%E6%89%93%E5%8C%85%E6%93%8D%E4%BD%9C/" class="sidebar-link">通过 Windows 脚本执行打包操作</a></li><li><a href="/%E9%98%BF%E9%87%8C%E4%BA%91%E5%AF%B9%E8%B1%A1%E5%AD%98%E5%82%A8-oss/" class="sidebar-link">阿里云对象存储 OSS</a></li><li><a href="/%E5%90%8E%E7%AB%AF-spring-boot-%E4%B8%87%E7%94%A8%E6%A8%A1%E6%9D%BF%E4%BD%BF%E7%94%A8/" class="sidebar-link">后端 Spring Boot 万用模板使用</a></li><li><a href="/%E4%BD%BF%E7%94%A8-canal-%E5%AE%9E%E7%8E%B0%E5%A2%9E%E9%87%8F%E8%AE%A2%E9%98%85%E5%92%8C%E6%B6%88%E8%B4%B9/" class="sidebar-link">使用 canal 实现增量订阅和消费</a></li><li><a href="/springboot-quartz-%E7%AE%80%E6%98%93%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1/" class="sidebar-link">SpringBoot + Quartz 简易定时任务</a></li><li><a href="/string-%E7%9A%84-intern-%E6%96%B9%E6%B3%95/" class="sidebar-link">String 的 intern() 方法</a></li><li><a href="/mysql-%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%97%A5%E6%9C%9F%E6%A0%BC%E5%BC%8F%E8%BD%AC%E6%8D%A2/" class="sidebar-link">MySQL 字符串日期格式转换</a></li><li><a href="/fail-fast-%E6%9C%BA%E5%88%B6%E6%98%AF%E4%BB%80%E4%B9%88/" class="sidebar-link">fail-fast 机制是什么？</a></li><li><a href="/%E5%8D%81%E7%A7%8D-sql-%E7%9A%84%E5%86%99%E6%B3%95/" class="sidebar-link">十种 SQL 的写法</a></li><li><a href="/mybatis-%E6%95%B4%E5%90%88%E5%A4%9A%E6%95%B0%E6%8D%AE%E6%BA%90/" class="sidebar-link">MyBatis 整合多数据源</a></li><li><a href="/_1%E7%A7%92%E5%B0%86%E6%9C%AC%E5%9C%B0springboot%E9%A1%B9%E7%9B%AEjar%E5%8C%85%E9%83%A8%E7%BD%B2%E5%88%B0linux%E7%8E%AF%E5%A2%83/" class="sidebar-link">1秒将本地SpringBoot项目jar包部署到Linux环境</a></li><li><a href="/ddd-%E6%98%AF%E4%BB%80%E4%B9%88/" class="sidebar-link">DDD 是什么？</a></li><li><a href="/oracle%E5%88%B0mysql%E5%87%BD%E6%95%B0%E6%9B%BF%E6%8D%A2%E6%96%B9%E6%A1%88%E6%B1%87%E6%80%BB/" class="sidebar-link">Oracle 到 MySQL 函数替换方案汇总</a></li><li><a href="/springboot-%E5%BC%95%E5%85%A5-nacos-windows-%E7%89%88/" class="sidebar-link">Springboot 引入 Nacos ( Windows 版)</a></li><li><a href="/%E5%88%A9%E7%94%A8%E5%B1%80%E5%9F%9F%E7%BD%91%E6%90%AD%E5%BB%BA%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%AE%9E%E7%8E%B0%E8%AE%BF%E9%97%AE%E9%A1%B9%E7%9B%AE/" class="sidebar-link">利用局域网搭建虚拟机实现访问项目</a></li><li><a href="/%E6%B5%85%E8%B0%88-cookie-%E5%92%8C-session/" class="sidebar-link">浅谈 cookie 和 session</a></li><li><a href="/%E7%94%A8-qq-%E9%82%AE%E7%AE%B1%E5%AE%9E%E7%8E%B0%E9%AA%8C%E8%AF%81%E7%A0%81%E5%8A%9F%E8%83%BD/" class="sidebar-link">用 QQ 邮箱实现验证码功能</a></li><li><a href="/redis-%E5%AE%9E%E7%8E%B0%E6%96%87%E7%AB%A0%E7%82%B9%E8%B5%9E%E5%8A%9F%E8%83%BD-%E9%99%84%E5%B8%A6%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BB%A3%E7%A0%81%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/" class="sidebar-link">Redis 实现文章点赞功能(附带前后端代码、数据库)</a></li><li><a href="/%E9%98%BF%E9%87%8C%E4%BA%91%E7%9F%AD%E4%BF%A1%E6%9C%8D%E5%8A%A1%E5%AE%9E%E7%8E%B0%E6%89%8B%E6%9C%BA%E9%AA%8C%E8%AF%81%E7%A0%81/" class="sidebar-link">阿里云短信服务实现手机验证码</a></li><li><a href="/%E3%80%90ajax%E3%80%91%E5%BC%82%E6%AD%A5%E9%80%9A%E4%BF%A1/" class="sidebar-link">【Ajax】异步通信</a></li><li><a href="/%E3%80%90%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%91%E4%B8%83%E5%A4%A7%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/" class="sidebar-link">【设计模式】七大设计原则</a></li><li><a href="/%E6%96%87%E7%AB%A0%E8%AF%84%E8%AE%BA%E5%8A%9F%E8%83%BD%E5%89%8D%E5%90%8E%E7%AB%AF%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%A1%88%E6%80%BB%E7%BB%93/" class="sidebar-link">文章评论功能前后端实现方案总结</a></li><li><a href="/%E3%80%90%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6%E3%80%91git%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B/" class="sidebar-link">【版本控制】Git快速上手</a></li><li><a href="/%E3%80%90java%E5%9F%BA%E7%A1%80%E3%80%91%E6%B6%88%E7%81%AD%E9%AD%94%E6%B3%95%E5%80%BC-%E5%B8%B8%E9%87%8F-%E6%9E%9A%E4%B8%BE%E8%AF%A6%E8%BF%B0/" class="sidebar-link">【Java基础】消灭魔法值-常量&amp;枚举详述</a></li><li><a href="/mysql-%E7%9A%84-char-%E5%B9%B6%E4%B8%8D%E4%B8%80%E5%AE%9A%E6%98%AF%E5%AE%9A%E9%95%BF/" class="sidebar-link">MySQL 的 Char 并不一定是定长</a></li><li><a href="/textcnn-%E6%96%87%E6%9C%AC%E5%88%86%E7%B1%BB%E6%A8%A1%E5%9E%8B%E5%9C%A8%E6%96%87%E7%AB%A0%E8%AF%84%E8%AE%BA%E5%AE%A1%E6%A0%B8%E4%B8%AD%E7%9A%84%E5%AE%9E%E7%8E%B0%E4%B8%8E%E9%83%A8%E7%BD%B2/" class="sidebar-link">TextCNN 文本分类模型在文章评论审核中的实现与部署</a></li><li><a href="/%E7%90%86%E8%A7%A3-io-%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8/" class="sidebar-link">理解 IO 多路复用</a></li><li><a href="/elasticsearch-%E5%9F%BA%E7%A1%80%E6%A6%82%E5%BF%B5%E4%B8%8E%E5%85%A5%E9%97%A8%E4%BD%BF%E7%94%A8/" class="sidebar-link">ElasticSearch 基础概念与入门使用</a></li><li><a href="/%E7%90%86%E8%A7%A3%E8%BF%9B%E7%A8%8B-%E7%BA%BF%E7%A8%8B-%E5%8D%8F%E7%A8%8B/" class="sidebar-link">理解进程，线程，协程</a></li><li><a href="/%E7%90%86%E8%A7%A3%E5%8E%9F%E7%A0%81%E3%80%81%E5%8F%8D%E7%A0%81%E3%80%81%E8%A1%A5%E7%A0%81/" class="sidebar-link">理解原码、反码、补码</a></li><li><a href="/%E5%AE%9A%E4%B9%89api%E5%B9%B6%E7%94%9F%E6%88%90%E4%BB%A3%E7%A0%81-go-%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%A1%86%E6%9E%B6-kratos/" class="sidebar-link">定义API并生成代码（ Go 微服务框架 Kratos ）</a></li></ul></section></li></ul> </aside> <main class="page"> <div class="content"><div style="width:100%"><div class="theme-default-content custom-content content__default"><h1 id="websocket-spring-sseemitter3-实现讯飞星火java客户端"><a href="#websocket-spring-sseemitter3-实现讯飞星火java客户端" class="header-anchor">#</a> Websocket.+ Spring-SseEmitter3 实现讯飞星火Java客户端</h1> <blockquote><p>作者：<a href="http://blog.dhx.icu" target="_blank" rel="noopener noreferrer">摸鱼摆烂小能手<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，<a href="https://wx.zsxq.com/dweb2/index/group/51122858222824" target="_blank" rel="noopener noreferrer">编程导航星球<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 编号 1083</p></blockquote> <p>通过websocket+SseEmitter实现讯飞星火Java客户端 , 一键CV即可接入springboot项目使用</p> <p>本篇文章主要记录完成 对接讯飞星火的Java 客户端 (适配于Spring) , 方便在之后项目开发的过程中进行快速接入</p> <ul><li>星火认知大模型Web API文档 : <a href="https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E" target="_blank" rel="noopener noreferrer">https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul> <h2 id="websocket"><a href="#websocket" class="header-anchor">#</a> websocket</h2> <h3 id="简介"><a href="#简介" class="header-anchor">#</a> 简介</h3> <p>在此之前我们先来简单了解一下websocket协议</p> <p><strong>WebSocket</strong>是一种在客户端和服务器之间建立长连接的技术，使得两者可以通过TCP套接字进行全双工通信。</p> <p>通信的<strong>双方都可以同时发送和接收数据</strong>，而无需等待对方完成其操作。</p> <p>其实现了浏览器与服务器<strong>全双工</strong>通信，能更好的节省服务器资源和带宽并达到实时通讯</p> <p><strong>WebSocket</strong>建立在 TCP 之上，同 HTTP 一样通过 TCP 来传输数据，但是它和 HTTP 最大不同是：</p> <ul><li>WebSocket 是一种<strong>双向通信协议</strong>，在建立连接后，WebSocket 服务器和 Browser/Client Agent 都能主动的向对方发送或接收数据</li> <li>WebSocket 需要类似 TCP 的客户端和服务器端通过握手连接，连接成功后才能相互通信。</li> <li>HTTP是一种无状态的请求/响应协议。它允许客户端从服务器获取资源，但是并不支持持久连接或双向通信。每次请求都需要重新建立TCP连接，因此对于频繁交互的应用来说效率较低。</li></ul> <blockquote><p>关于 全双工通信和半双工通信</p> <p>在通信系统中，<strong>双工是指同时进行的两个方向的数据传输</strong>。其中，全双工表示数据可以在两个方向同时传输，<strong>而半双工则指数据在同一时间只能在一个方向上传输。</strong></p> <p>场景的场景比如 : <strong>电话对话和面对面交谈</strong></p></blockquote> <h3 id="demo"><a href="#demo" class="header-anchor">#</a> demo</h3> <p>接下来我们通过 <code>javax.websocket</code>包来实现简单的demo , 更好理解websocket的工作流程以及方式</p> <p>准备工作:</p> <ul><li>Postman : 充当 websocket 客户端</li> <li>Springboot Application : 搭建websocket server</li></ul> <p>引入依赖</p> <div class="language-xml line-numbers-mode"><pre class="language-xml"><code><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">&gt;</span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">&gt;</span></span>spring-boot-starter-websocket<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">&gt;</span></span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>示例代码</p> <h4 id="websocketserver-java"><a href="#websocketserver-java" class="header-anchor">#</a> <code>WebSocketServer.java</code></h4> <p>该文件 会在客户端连接 , 断开 , 发送消息的时候打印相关的日志信息 , 接收参数为 <code>userId</code></p> <p>其中有几个关键的事件</p> <ul><li>onclose：用于指定连接关闭后的回调函数；</li> <li>onerror：用于指定连接失败后的回调函数；</li> <li>onmessage：用于指定当从服务器接受到信息时的回调函数；</li> <li>onopen：用于指定连接成功后的回调函数；</li></ul> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Slf4j</span>
<span class="token annotation punctuation">@ServerEndpoint</span><span class="token punctuation">(</span><span class="token string">&quot;/user/{userId}&quot;</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">WebSocketServer</span> <span class="token punctuation">{</span>

    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token class-name">AtomicInteger</span> onlineCount <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AtomicInteger</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">/**
     * 网络套接字设置 concurrent包的线程安全Set，用来存放每个客户端对应的WebSocket对象。
     */</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token class-name">CopyOnWriteArraySet</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">WebSocketServer</span><span class="token punctuation">&gt;</span></span> webSocketSet <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CopyOnWriteArraySet</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">/**
     * 会话 与某个客户端的连接会话，需要通过它来给客户端发送数据
     */</span>
    <span class="token keyword">private</span> <span class="token class-name">Session</span> session<span class="token punctuation">;</span>
    <span class="token comment">/**
     * id
     */</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> userId <span class="token operator">=</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">;</span>

    <span class="token annotation punctuation">@OnOpen</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onOpen</span><span class="token punctuation">(</span><span class="token class-name">Session</span> session<span class="token punctuation">,</span> <span class="token annotation punctuation">@PathParam</span><span class="token punctuation">(</span><span class="token string">&quot;userId&quot;</span><span class="token punctuation">)</span> <span class="token class-name">String</span> userId<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>session <span class="token operator">=</span> session<span class="token punctuation">;</span>
        webSocketSet<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>     <span class="token comment">// 加入set中</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>userId <span class="token operator">=</span> userId<span class="token punctuation">;</span>
        <span class="token function">addOnlineCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>           <span class="token comment">// 在线数加1</span>
        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;有新客户端开始监听,userId=&quot;</span> <span class="token operator">+</span> userId <span class="token operator">+</span> <span class="token string">&quot;,当前在线人数为:&quot;</span> <span class="token operator">+</span> <span class="token function">getOnlineCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@OnClose</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onClose</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        webSocketSet<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">// 从set中删除</span>
        <span class="token function">subOnlineCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>              <span class="token comment">// 在线数减1</span>
        <span class="token comment">// 断开连接情况下，更新主板占用情况为释放</span>
        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;释放的userId=&quot;</span> <span class="token operator">+</span> userId <span class="token operator">+</span> <span class="token string">&quot;的客户端&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">releaseResource</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">releaseResource</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;有一连接关闭！当前在线人数为&quot;</span> <span class="token operator">+</span> <span class="token function">getOnlineCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@OnMessage</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onMessage</span><span class="token punctuation">(</span><span class="token class-name">String</span> message<span class="token punctuation">,</span> <span class="token class-name">Session</span> session<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;收到来自客户端 userId=&quot;</span> <span class="token operator">+</span> userId <span class="token operator">+</span> <span class="token string">&quot; 的信息:&quot;</span> <span class="token operator">+</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 群发消息</span>
        <span class="token class-name">HashSet</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">&gt;</span></span> userIds <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashSet</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">WebSocketServer</span> item <span class="token operator">:</span> webSocketSet<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            userIds<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>item<span class="token punctuation">.</span>userId<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            <span class="token function">sendMessage</span><span class="token punctuation">(</span><span class="token string">&quot;客户端 &quot;</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>userId <span class="token operator">+</span> <span class="token string">&quot;发布消息：&quot;</span> <span class="token operator">+</span> message<span class="token punctuation">,</span> userIds<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@OnError</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onError</span><span class="token punctuation">(</span><span class="token class-name">Session</span> session<span class="token punctuation">,</span> <span class="token class-name">Throwable</span> error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>session<span class="token punctuation">.</span><span class="token function">getBasicRemote</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">&quot;客户端发生错误&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        error<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>


    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">sendMessage</span><span class="token punctuation">(</span><span class="token class-name">String</span> message<span class="token punctuation">,</span> <span class="token class-name">HashSet</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">&gt;</span></span> toSids<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span>
        log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;推送消息到客户端 &quot;</span> <span class="token operator">+</span> toSids <span class="token operator">+</span> <span class="token string">&quot;，推送内容:&quot;</span> <span class="token operator">+</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">WebSocketServer</span> item <span class="token operator">:</span> webSocketSet<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">try</span> <span class="token punctuation">{</span>
                <span class="token comment">//这里可以设定只推送给传入的userId，为null则全部推送</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>toSids<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    item<span class="token punctuation">.</span><span class="token function">sendMessage</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>toSids<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>item<span class="token punctuation">.</span>userId<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    item<span class="token punctuation">.</span><span class="token function">sendMessage</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">continue</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">sendMessage</span><span class="token punctuation">(</span><span class="token class-name">String</span> message<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>session<span class="token punctuation">.</span><span class="token function">getBasicRemote</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">sendText</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">int</span> <span class="token function">getOnlineCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> onlineCount<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">addOnlineCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        onlineCount<span class="token punctuation">.</span><span class="token function">getAndIncrement</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">subOnlineCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        onlineCount<span class="token punctuation">.</span><span class="token function">getAndDecrement</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br><span class="line-number">56</span><br><span class="line-number">57</span><br><span class="line-number">58</span><br><span class="line-number">59</span><br><span class="line-number">60</span><br><span class="line-number">61</span><br><span class="line-number">62</span><br><span class="line-number">63</span><br><span class="line-number">64</span><br><span class="line-number">65</span><br><span class="line-number">66</span><br><span class="line-number">67</span><br><span class="line-number">68</span><br><span class="line-number">69</span><br><span class="line-number">70</span><br><span class="line-number">71</span><br><span class="line-number">72</span><br><span class="line-number">73</span><br><span class="line-number">74</span><br><span class="line-number">75</span><br><span class="line-number">76</span><br><span class="line-number">77</span><br><span class="line-number">78</span><br><span class="line-number">79</span><br><span class="line-number">80</span><br><span class="line-number">81</span><br><span class="line-number">82</span><br><span class="line-number">83</span><br><span class="line-number">84</span><br><span class="line-number">85</span><br><span class="line-number">86</span><br><span class="line-number">87</span><br><span class="line-number">88</span><br><span class="line-number">89</span><br><span class="line-number">90</span><br><span class="line-number">91</span><br><span class="line-number">92</span><br><span class="line-number">93</span><br><span class="line-number">94</span><br><span class="line-number">95</span><br><span class="line-number">96</span><br><span class="line-number">97</span><br></div></div><h4 id="websocketconfig"><a href="#websocketconfig" class="header-anchor">#</a> websocketConfig</h4> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Configuration</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">WebSocketConfig</span> <span class="token punctuation">{</span>

    <span class="token annotation punctuation">@Bean</span>
    <span class="token keyword">public</span> <span class="token class-name">ServerEndpointExporter</span> <span class="token function">serverEndpointExporter</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ServerEndpointExporter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><h4 id="控制层代码"><a href="#控制层代码" class="header-anchor">#</a> 控制层代码</h4> <p>编写控制层接口 , 调用接口会向(指定userId的)客户端发送信息</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@RestController</span>
<span class="token annotation punctuation">@Slf4j</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">TestController</span> <span class="token punctuation">{</span>

    <span class="token annotation punctuation">@Resource</span>
    <span class="token class-name">WebSocketServer</span> webSocketServer<span class="token punctuation">;</span>

    <span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/test/send/ws/{userId}&quot;</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">send2WS</span><span class="token punctuation">(</span><span class="token annotation punctuation">@PathVariable</span> <span class="token class-name">String</span> userId<span class="token punctuation">)</span><span class="token punctuation">{</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            webSocketServer<span class="token punctuation">.</span><span class="token function">sendMessage</span><span class="token punctuation">(</span><span class="token string">&quot;this is a message from server!&quot;</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">HashSet</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token class-name">Arrays</span><span class="token punctuation">.</span><span class="token function">asList</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span>userId<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">return</span> <span class="token string">&quot;success&quot;</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br></div></div><h4 id="测试"><a href="#测试" class="header-anchor">#</a> 测试</h4> <p>启动项目 , 接着我们通过postman进行测试<img alt="" data-src="https://pic.yupi.icu/5563/202311191321280.png" loading="lazy" class="lazy"></p> <p>接着访问 <code>http://localhost:8080/test/send/ws/1</code></p> <p>可以看到我们已经提前预设好的消息</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311191321358.png" loading="lazy" class="lazy"></p> <h2 id="讯飞星火"><a href="#讯飞星火" class="header-anchor">#</a> 讯飞星火</h2> <p>了解完websocket之后, 我们来动手实现一个 通用的 <code>SparkManager</code></p> <h3 id="申请key"><a href="#申请key" class="header-anchor">#</a> 申请key</h3> <p>访问 <a href="https://console.xfyun.cn/sale/buy?wareId=9048&amp;packageId=9048001&amp;serviceName=%E6%98%9F%E7%81%AB%E8%AE%A4%E7%9F%A5%E5%A4%A7%E6%A8%A1%E5%9E%8BV3.0&amp;businessId=bm3%60" target="_blank" rel="noopener noreferrer">官网<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 购买 免费的个人体验包即可</p> <ul><li>包含了 200w 个token , 足够开发过程使用</li></ul> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311191321364.png" loading="lazy" class="lazy"></p> <h3 id="请求流程分析"><a href="#请求流程分析" class="header-anchor">#</a> 请求流程分析</h3> <p>这里参考官方给出的 示例代码 的结构 , 分析具体请求的流程</p> <ol><li><p>通过 apikey apiSecret 等配置信息 , 构建 请求的websokcet的url</p></li> <li><p>创建websocket连接</p> <p>这里的核心逻辑都写在 websocket 的 事件中</p> <ul><li>onOpen : 连接开启 , <strong>创建线程</strong> , 构建并请求大模型</li> <li>onMessage : 大模型返回chat的结果 , 这里通过 返回的 业务状态码 , 进行不同的操作 , 比如 :
<ul><li>发生异常, 退出chat</li> <li>保存chat的结果</li> <li>有关 历史chat记录的操作</li></ul></li> <li>onFailure : 发生错误 , 退出</li></ul></li></ol> <p>这里的 示例代码 数据是保存在类的属性中的, 也就是保存了 此次请求的状态信息 , 并不支持多线程访问 ,</p> <p>因此后续在此 demo 的基础之上进行改善, 使得支持嵌入到正常的项目中</p> <h3 id="鉴权-authutil"><a href="#鉴权-authutil" class="header-anchor">#</a> 鉴权-AuthUtil</h3> <ul><li>官方文档 : <a href="https://www.xfyun.cn/doc/spark/general_url_authentication.html#_1-%E9%89%B4%E6%9D%83%E8%AF%B4%E6%98%8E" target="_blank" rel="noopener noreferrer">https://www.xfyun.cn/doc/spark/general_url_authentication.html#_1-%E9%89%B4%E6%9D%83%E8%AF%B4%E6%98%8E<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul> <p>主要参数如下</p> <table><thead><tr><th>参数</th> <th>类型</th> <th>必须</th> <th>说明</th> <th>示例</th></tr></thead> <tbody><tr><td>host</td> <td>string</td> <td>是</td> <td>请求的主机</td> <td>aichat.xf-yun.com(使用时需替换为实际使用的接口地址）</td></tr> <tr><td>date</td> <td>string</td> <td>是</td> <td>当前时间戳，采用RFC1123格式，时间偏差需控制在300s内</td> <td>Fri, 05 May 2023 10:43:39 GMT</td></tr> <tr><td>authorization</td> <td>string</td> <td>是</td> <td>base64编码的签名信息</td> <td>参考下方生成方式</td></tr></tbody></table> <blockquote><p>这里一定要注意日期的格式是 英文 , 在格式化日期的时候需要设置为 US</p></blockquote> <p>主要流程</p> <ol><li>获取 RFC_1123 格式日期</li> <li>获取授权信息</li> <li>加密</li> <li>拼接url</li></ol> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">String</span> <span class="token function">genAuthUrl</span><span class="token punctuation">(</span><span class="token class-name">String</span> apiKey<span class="token punctuation">,</span> <span class="token class-name">String</span> apiSecret<span class="token punctuation">,</span> <span class="token class-name">String</span> host<span class="token punctuation">,</span> <span class="token class-name">String</span> path<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token class-name">StringUtils</span><span class="token punctuation">.</span><span class="token function">isAnyBlank</span><span class="token punctuation">(</span>apiKey<span class="token punctuation">,</span> apiSecret<span class="token punctuation">,</span> host<span class="token punctuation">,</span> path<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">InvalidParameterException</span><span class="token punctuation">(</span><span class="token string">&quot;参数不能为空!&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">try</span> <span class="token punctuation">{</span>
        <span class="token class-name">String</span> date <span class="token operator">=</span> <span class="token function">getRFC1123Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">String</span> httpUrl <span class="token operator">=</span> <span class="token function">generateAuthorization</span><span class="token punctuation">(</span>date<span class="token punctuation">,</span> host<span class="token punctuation">,</span> path<span class="token punctuation">,</span> apiSecret<span class="token punctuation">,</span> apiKey<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> httpUrl<span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">throw</span> e<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><h3 id="构建请求类"><a href="#构建请求类" class="header-anchor">#</a> 构建请求类</h3> <p>接口请求字段由三个部分组成：header，parameter, payload。 字段解释如下</p> <p><strong>header部分</strong></p> <table><thead><tr><th>参数名称</th> <th>类型</th> <th>必传</th> <th>参数要求</th> <th>参数说明</th></tr></thead> <tbody><tr><td>app_id</td> <td>string</td> <td>是</td> <td></td> <td>应用appid，从开放平台控制台创建的应用中获取</td></tr> <tr><td>uid</td> <td>string</td> <td>否</td> <td>最大长度32</td> <td>每个用户的id，用于区分不同用户</td></tr></tbody></table> <p><strong>parameter.chat部分</strong></p> <table><thead><tr><th>参数名称</th> <th>类型</th> <th>必传</th> <th>参数要求</th> <th>参数说明</th></tr></thead> <tbody><tr><td>domain</td> <td>string</td> <td>是</td> <td>取值为[general,generalv2,generalv3]</td> <td>指定访问的领域,general指向V1.5版本,generalv2指向V2版本,generalv3指向V3版本 。注意：不同的取值对应的url也不一样！</td></tr> <tr><td>temperature</td> <td>float</td> <td>否</td> <td>取值为[0,1],默认为0.5</td> <td>核采样阈值。用于决定结果随机性，取值越高随机性越强即相同的问题得到的不同答案的可能性越高</td></tr> <tr><td>max_tokens</td> <td>int</td> <td>否</td> <td>V1.5取值为[1,4096] V2.0取值为[1,8192]，默认为2048。 V3.0取值为[1,8192]，默认为2048。</td> <td>模型回答的tokens的最大长度</td></tr> <tr><td>top_k</td> <td>int</td> <td>否</td> <td>取值为[1，6],默认为4</td> <td>从k个候选中随机选择⼀个（⾮等概率）</td></tr> <tr><td>chat_id</td> <td>string</td> <td>否</td> <td>需要保障用户下的唯一性</td> <td>用于关联用户会话</td></tr></tbody></table> <p><strong>payload.message.text部分</strong></p> <p><em>注：text下所有content累计内容 tokens需要控制在8192内</em></p> <table><thead><tr><th>参数名称</th> <th>类型</th> <th>必传</th> <th>参数要求</th> <th>参数说明</th></tr></thead> <tbody><tr><td>role</td> <td>string</td> <td>是</td> <td>取值为[user,assistant]</td> <td>user表示是用户的问题，assistant表示AI的回复</td></tr> <tr><td>content</td> <td>string</td> <td>是</td> <td>所有content的累计tokens需控制8192以内</td> <td>用户和AI的对话内容</td></tr></tbody></table> <p>构建请求类ChatRequest如下</p> <blockquote><p>直接使用 JsonObject 或者map 用来构建都是可以的, 不过我认为通过类以及属性来构建会更方便后续的扩展</p> <ul><li>使用Lombok提供的<code>@Data</code> 以及 <code>@Builder</code>都是十分方便的</li></ul></blockquote> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Data</span>
<span class="token annotation punctuation">@AllArgsConstructor</span>
<span class="token annotation punctuation">@NoArgsConstructor</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ChatRequest</span> <span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token class-name">Header</span> header<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">Parameter</span> parameter<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">Payload</span> payload<span class="token punctuation">;</span>

    <span class="token annotation punctuation">@Data</span>
    <span class="token annotation punctuation">@AllArgsConstructor</span>
    <span class="token annotation punctuation">@NoArgsConstructor</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Header</span> <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token class-name">String</span> appId<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token class-name">String</span> uid<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Data</span>
    <span class="token annotation punctuation">@AllArgsConstructor</span>
    <span class="token annotation punctuation">@NoArgsConstructor</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Parameter</span> <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token class-name">Chat</span> chat<span class="token punctuation">;</span>

    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Data</span>
    <span class="token annotation punctuation">@AllArgsConstructor</span>
    <span class="token annotation punctuation">@NoArgsConstructor</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Chat</span> <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token class-name">String</span> domain<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token keyword">double</span> temperature<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token keyword">int</span> maxTokens<span class="token punctuation">;</span>

    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Data</span>
    <span class="token annotation punctuation">@AllArgsConstructor</span>
    <span class="token annotation punctuation">@NoArgsConstructor</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Payload</span> <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token class-name">Message</span> message<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Data</span>
    <span class="token annotation punctuation">@AllArgsConstructor</span>
    <span class="token annotation punctuation">@NoArgsConstructor</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Message</span> <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Text</span><span class="token punctuation">&gt;</span></span> text<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Data</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Text</span> <span class="token punctuation">{</span>
        <span class="token class-name">String</span> role<span class="token punctuation">;</span>
        <span class="token class-name">String</span> content<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br></div></div><h3 id="构建结果类"><a href="#构建结果类" class="header-anchor">#</a> 构建结果类</h3> <p><strong>header部分</strong></p> <table><thead><tr><th>字段名</th> <th>类型</th> <th>字段说明</th></tr></thead> <tbody><tr><td>code</td> <td>int</td> <td>错误码，0表示正常，非0表示出错；详细释义可在接口说明文档最后的错误码说明了解</td></tr> <tr><td>message</td> <td>string</td> <td>会话是否成功的描述信息</td></tr> <tr><td>sid</td> <td>string</td> <td>会话的唯一id，用于讯飞技术人员查询服务端会话日志使用,出现调用错误时建议留存该字段</td></tr> <tr><td>status</td> <td>int</td> <td>会话状态，取值为[0,1,2]；0代表首次结果；1代表中间结果；2代表最后一个结果</td></tr></tbody></table> <p><strong>payload.choices部分</strong></p> <table><thead><tr><th>字段名</th> <th>类型</th> <th>字段说明</th></tr></thead> <tbody><tr><td>status</td> <td>int</td> <td>文本响应状态，取值为[0,1,2]; 0代表首个文本结果；1代表中间文本结果；2代表最后一个文本结果</td></tr> <tr><td>seq</td> <td>int</td> <td>返回的数据序号，取值为[0,9999999]</td></tr> <tr><td>content</td> <td>string</td> <td>AI的回答内容</td></tr> <tr><td>role</td> <td>string</td> <td>角色标识，固定为assistant，标识角色为AI</td></tr> <tr><td>index</td> <td>int</td> <td>结果序号，取值为[0,10]; 当前为保留字段，开发者可忽略</td></tr></tbody></table> <p><strong>payload.usage部分(在最后一次结果返回)</strong></p> <table><thead><tr><th>字段名</th> <th>类型</th> <th>字段说明</th></tr></thead> <tbody><tr><td>question_tokens</td> <td>int</td> <td>保留字段，可忽略</td></tr> <tr><td>prompt_tokens</td> <td>int</td> <td>包含历史问题的总tokens大小</td></tr> <tr><td>completion_tokens</td> <td>int</td> <td>回答的tokens大小</td></tr> <tr><td>total_tokens</td> <td>int</td> <td>prompt_tokens和completion_tokens的和，也是本次交互计费的tokens大小</td></tr></tbody></table> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Data</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ChatResponse</span> <span class="token punctuation">{</span>

    <span class="token keyword">private</span> <span class="token class-name">Header</span> header<span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token class-name">Payload</span> payload<span class="token punctuation">;</span>

    <span class="token annotation punctuation">@Data</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Header</span> <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token keyword">int</span> code<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token class-name">String</span> message<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token class-name">String</span> sid<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token keyword">int</span> status<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Data</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Payload</span> <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token class-name">Choices</span> choices<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token class-name">Usage</span> usage<span class="token punctuation">;</span>


    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Data</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Choices</span> <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token keyword">int</span> status<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token keyword">int</span> seq<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">ContentRoleIndex</span><span class="token punctuation">&gt;</span></span> text<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Data</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">ContentRoleIndex</span> <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token class-name">String</span> content<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token class-name">String</span> role<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token keyword">int</span> index<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Data</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Usage</span> <span class="token punctuation">{</span>
        <span class="token keyword">private</span> <span class="token class-name">Tokens</span> text<span class="token punctuation">;</span>

        <span class="token annotation punctuation">@Data</span>
        <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Tokens</span> <span class="token punctuation">{</span>
            <span class="token keyword">private</span> <span class="token keyword">int</span> question_tokens<span class="token punctuation">;</span>
            <span class="token keyword">private</span> <span class="token keyword">int</span> prompt_tokens<span class="token punctuation">;</span>
            <span class="token keyword">private</span> <span class="token keyword">int</span> completion_tokens<span class="token punctuation">;</span>
            <span class="token keyword">private</span> <span class="token keyword">int</span> total_tokens<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br></div></div><h3 id="发起请求"><a href="#发起请求" class="header-anchor">#</a> 发起请求</h3> <p>对接前面的流程分析 , 我们的发起请求的操作放到一个新的线程中执行</p> <blockquote><p>注意 : 这里的代码并不是最终的版本</p></blockquote> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">doChat</span><span class="token punctuation">(</span><span class="token class-name">String</span> message<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{</span>
    <span class="token class-name">NewQuestion</span> <span class="token operator">=</span> message<span class="token punctuation">;</span>
    <span class="token comment">// 构建鉴权url</span>
    <span class="token class-name">String</span> authUrl <span class="token operator">=</span> <span class="token class-name">AuthUtil</span><span class="token punctuation">.</span><span class="token function">genAuthUrl</span><span class="token punctuation">(</span>apiKey<span class="token punctuation">,</span> apiSecret<span class="token punctuation">,</span> host<span class="token punctuation">,</span> path<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">OkHttpClient</span> client <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OkHttpClient<span class="token punctuation">.</span>Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 构建websocket请求</span>
    <span class="token class-name">Request</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Request<span class="token punctuation">.</span>Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">url</span><span class="token punctuation">(</span>authUrl<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    totalAnswer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StringBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 发起websocket请求</span>
    <span class="token class-name">WebSocket</span> webSocket <span class="token operator">=</span> client<span class="token punctuation">.</span><span class="token function">newWebSocket</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">MySparkManager</span><span class="token punctuation">(</span><span class="token number">11111L</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> totalAnswer<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><p>在连接建立之后 , 执行<code>onOpen</code>方法 , 这里我们新开启一个线程 , 用来与 模型交互</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onOpen</span><span class="token punctuation">(</span><span class="token class-name">WebSocket</span> webSocket<span class="token punctuation">,</span> <span class="token class-name">Response</span> response<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onOpen</span><span class="token punctuation">(</span>webSocket<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span>
    myThread <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyThread</span><span class="token punctuation">(</span>webSocket<span class="token punctuation">,</span> userId<span class="token punctuation">)</span><span class="token punctuation">;</span>
    myThread<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>其中 Thread的 run() 方法主要任务为 构建并发起请求</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">try</span> <span class="token punctuation">{</span>
        <span class="token class-name">ChatRequest</span> chatRequest <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ChatRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">ChatRequest</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">header</span><span class="token punctuation">(</span><span class="token class-name">ChatRequest<span class="token punctuation">.</span>Header</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                    <span class="token punctuation">.</span><span class="token function">app_id</span><span class="token punctuation">(</span>appid<span class="token punctuation">)</span>
                    <span class="token punctuation">.</span><span class="token function">uid</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span> userId<span class="token punctuation">)</span>
                    <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">parameter</span><span class="token punctuation">(</span><span class="token class-name">Parameter</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                       <span class="token punctuation">.</span><span class="token function">chat</span><span class="token punctuation">(</span><span class="token class-name">Chat</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                             <span class="token punctuation">.</span><span class="token function">domain</span><span class="token punctuation">(</span>domain<span class="token punctuation">)</span>
                             <span class="token punctuation">.</span><span class="token function">temperature</span><span class="token punctuation">(</span><span class="token number">0.5</span><span class="token punctuation">)</span>
                             <span class="token punctuation">.</span><span class="token function">maxTokens</span><span class="token punctuation">(</span><span class="token number">4096</span><span class="token punctuation">)</span>
                             <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                       <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">payload</span><span class="token punctuation">(</span><span class="token class-name">ChatRequest<span class="token punctuation">.</span>Payload</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                     <span class="token punctuation">.</span><span class="token function">message</span><span class="token punctuation">(</span><span class="token class-name">ChatRequest<span class="token punctuation">.</span>Message</span>
                              <span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                              <span class="token punctuation">.</span><span class="token function">text</span><span class="token punctuation">(</span><span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singletonList</span><span class="token punctuation">(</span>
                                  <span class="token class-name">Text</span><span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
                                  <span class="token punctuation">.</span><span class="token function">content</span><span class="token punctuation">(</span><span class="token class-name">NewQuestion</span><span class="token punctuation">)</span>
                                  <span class="token punctuation">.</span><span class="token function">role</span><span class="token punctuation">(</span><span class="token string">&quot;user&quot;</span><span class="token punctuation">)</span>
                                  <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                              <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                     <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        webSocket<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>chatRequest<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 等待服务端返回完毕后关闭</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token comment">// System.err.println(wsCloseFlag + &quot;---&quot;);</span>
            <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>wsCloseFlag<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">break</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
        webSocket<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        myThread<span class="token punctuation">.</span><span class="token function">interrupt</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br></div></div><h3 id="接收响应结果"><a href="#接收响应结果" class="header-anchor">#</a> 接收响应结果</h3> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Override</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onMessage</span><span class="token punctuation">(</span><span class="token class-name">WebSocket</span> webSocket<span class="token punctuation">,</span> <span class="token class-name">String</span> text<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// System.out.println(userId + &quot;用来区分那个用户的结果&quot; + text);</span>
    <span class="token class-name">ChatResponse</span> chatResponse <span class="token operator">=</span> <span class="token class-name">JSONUtil</span><span class="token punctuation">.</span><span class="token function">toBean</span><span class="token punctuation">(</span>text<span class="token punctuation">,</span> <span class="token class-name">ChatResponse</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>chatResponse<span class="token punctuation">.</span><span class="token function">getHeader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;发生错误，错误码为：&quot;</span> <span class="token operator">+</span> chatResponse<span class="token punctuation">.</span><span class="token function">getHeader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;本次请求的sid为：&quot;</span> <span class="token operator">+</span> chatResponse<span class="token punctuation">.</span><span class="token function">getHeader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getSid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        webSocket<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">ContentRoleIndex</span><span class="token punctuation">&gt;</span></span> textList <span class="token operator">=</span> chatResponse<span class="token punctuation">.</span><span class="token function">getPayload</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getChoices</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getText</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">ContentRoleIndex</span> temp <span class="token operator">:</span> textList<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        totalAnswer<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>temp<span class="token punctuation">.</span><span class="token function">getContent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>chatResponse<span class="token punctuation">.</span><span class="token function">getHeader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 可以关闭连接，释放资源</span>
        <span class="token class-name">ContentRoleIndex</span> contentRoleIndex <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ContentRoleIndex</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        contentRoleIndex<span class="token punctuation">.</span><span class="token function">setRole</span><span class="token punctuation">(</span><span class="token string">&quot;assistant&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        contentRoleIndex<span class="token punctuation">.</span><span class="token function">setContent</span><span class="token punctuation">(</span>totalAnswer<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        wsCloseFlag <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br></div></div><h3 id="并发访问改造"><a href="#并发访问改造" class="header-anchor">#</a> 并发访问改造</h3> <p>原本的主要问题就是 userId 与 answer 绑定在一起了, 并且只有一个doChat () 方法, 因此这里的主要做法就是把doChat()独立出来</p> <p>并且对于原本的代码, 是不能注入Spring Bean 的 , 因此需要准备一个构造器</p> <p>主要参数有</p> <ul><li>配置信息</li> <li>userId</li> <li>问题</li></ul> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token class-name">SparkChat</span><span class="token punctuation">(</span><span class="token class-name">SparkConfig</span> sparkConfig<span class="token punctuation">,</span> <span class="token keyword">long</span> userId<span class="token punctuation">,</span> <span class="token class-name">String</span> question<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>userId <span class="token operator">=</span> userId<span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>question <span class="token operator">=</span> question<span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>apiKey <span class="token operator">=</span> sparkConfig<span class="token punctuation">.</span><span class="token function">getApiKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>apiSecret <span class="token operator">=</span> sparkConfig<span class="token punctuation">.</span><span class="token function">getApiSecret</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>appid <span class="token operator">=</span> sparkConfig<span class="token punctuation">.</span><span class="token function">getAppId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>sparkConfig<span class="token punctuation">.</span><span class="token function">getModelVersion</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>host <span class="token operator">=</span> sparkConfig<span class="token punctuation">.</span><span class="token function">getHostInfos</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token class-name">Integer</span><span class="token punctuation">.</span><span class="token function">parseInt</span><span class="token punctuation">(</span>sparkConfig<span class="token punctuation">.</span><span class="token function">getModelVersion</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getHost</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>path <span class="token operator">=</span> sparkConfig<span class="token punctuation">.</span><span class="token function">getHostInfos</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token class-name">Integer</span><span class="token punctuation">.</span><span class="token function">parseInt</span><span class="token punctuation">(</span>sparkConfig<span class="token punctuation">.</span><span class="token function">getModelVersion</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>domain <span class="token operator">=</span> sparkConfig<span class="token punctuation">.</span><span class="token function">getHostInfos</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token class-name">Integer</span><span class="token punctuation">.</span><span class="token function">parseInt</span><span class="token punctuation">(</span>sparkConfig<span class="token punctuation">.</span><span class="token function">getModelVersion</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getDomain</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>host <span class="token operator">=</span> sparkConfig<span class="token punctuation">.</span><span class="token function">getDefaultHostInfo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getHost</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>path <span class="token operator">=</span> sparkConfig<span class="token punctuation">.</span><span class="token function">getDefaultHostInfo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>domain <span class="token operator">=</span> sparkConfig<span class="token punctuation">.</span><span class="token function">getDefaultHostInfo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getDomain</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br></div></div><p>原本的代码 核心逻辑就与 websocket 的事件相绑定</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@Slf4j</span>
<span class="token annotation punctuation">@Data</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SparkChatListener</span> <span class="token keyword">extends</span> <span class="token class-name">WebSocketListener</span> <span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token class-name">StringBuilder</span> totalAnswer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StringBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">/**
     * websocket连接关闭标志
     */</span>
    <span class="token keyword">private</span> <span class="token class-name">Boolean</span> wsCloseFlag<span class="token punctuation">;</span>
    <span class="token annotation punctuation">@Override</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onOpen</span><span class="token punctuation">(</span><span class="token class-name">WebSocket</span> webSocket<span class="token punctuation">,</span> <span class="token class-name">Response</span> response<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onOpen</span><span class="token punctuation">(</span>webSocket<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Override</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onMessage</span><span class="token punctuation">(</span><span class="token class-name">WebSocket</span> webSocket<span class="token punctuation">,</span> <span class="token class-name">String</span> text<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">ChatResponse</span> chatResponse <span class="token operator">=</span> <span class="token class-name">JSONUtil</span><span class="token punctuation">.</span><span class="token function">toBean</span><span class="token punctuation">(</span>text<span class="token punctuation">,</span> <span class="token class-name">ChatResponse</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>chatResponse<span class="token punctuation">.</span><span class="token function">getHeader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;发生错误，错误码为：&quot;</span> <span class="token operator">+</span> chatResponse<span class="token punctuation">.</span><span class="token function">getHeader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;本次请求的sid为：&quot;</span> <span class="token operator">+</span> chatResponse<span class="token punctuation">.</span><span class="token function">getHeader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getSid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            webSocket<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> chatResponse<span class="token punctuation">.</span><span class="token function">getHeader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">ContentRoleIndex</span><span class="token punctuation">&gt;</span></span> textList <span class="token operator">=</span> chatResponse<span class="token punctuation">.</span><span class="token function">getPayload</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getChoices</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getText</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">ContentRoleIndex</span> temp <span class="token operator">:</span> textList<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            totalAnswer<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>temp<span class="token punctuation">.</span><span class="token function">getContent</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>chatResponse<span class="token punctuation">.</span><span class="token function">getHeader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getStatus</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token comment">// 可以关闭连接，释放资源</span>
            <span class="token class-name">ContentRoleIndex</span> contentRoleIndex <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ContentRoleIndex</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            contentRoleIndex<span class="token punctuation">.</span><span class="token function">setRole</span><span class="token punctuation">(</span><span class="token string">&quot;assistant&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            contentRoleIndex<span class="token punctuation">.</span><span class="token function">setContent</span><span class="token punctuation">(</span>totalAnswer<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            wsCloseFlag <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Override</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">onFailure</span><span class="token punctuation">(</span><span class="token class-name">WebSocket</span> webSocket<span class="token punctuation">,</span> <span class="token class-name">Throwable</span> t<span class="token punctuation">,</span> <span class="token class-name">Response</span> response<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onFailure</span><span class="token punctuation">(</span>webSocket<span class="token punctuation">,</span> t<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>response <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">int</span> code <span class="token operator">=</span> response<span class="token punctuation">.</span><span class="token function">code</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">try</span> <span class="token punctuation">{</span>
                log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;[Spark-chat-websocket]failure code:{} , boyd{}&quot;</span><span class="token punctuation">,</span> code<span class="token punctuation">,</span> response<span class="token punctuation">.</span><span class="token function">body</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br></div></div><h3 id="sparkmanager"><a href="#sparkmanager" class="header-anchor">#</a> SparkManager</h3> <p>这里的manager 主要的操作就是 <code>doChat()</code></p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">doChat</span><span class="token punctuation">(</span><span class="token keyword">long</span> userId<span class="token punctuation">,</span><span class="token class-name">String</span> question<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{</span>
    <span class="token comment">// 构建鉴权url</span>
    <span class="token class-name">String</span> authUrl <span class="token operator">=</span> <span class="token class-name">AuthUtil</span><span class="token punctuation">.</span><span class="token function">genAuthUrl</span><span class="token punctuation">(</span>apiKey<span class="token punctuation">,</span> apiSecret<span class="token punctuation">,</span> host<span class="token punctuation">,</span> path<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">OkHttpClient</span> client <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OkHttpClient<span class="token punctuation">.</span>Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 构建websocket请求</span>
    <span class="token class-name">Request</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Request<span class="token punctuation">.</span>Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">url</span><span class="token punctuation">(</span>authUrl<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">SparkChat</span> sparkChat <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SparkChat</span><span class="token punctuation">(</span>sparkConfig<span class="token punctuation">,</span> userId<span class="token punctuation">,</span>question<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 发起websocket请求</span>
    <span class="token class-name">WebSocket</span> webSocket <span class="token operator">=</span> client<span class="token punctuation">.</span><span class="token function">newWebSocket</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span>sparkChat <span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> sparkChat<span class="token punctuation">.</span><span class="token function">getTotalAnswer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><p>另外为了 脱离 WebSocketListener 与 配置信息和用户信息的耦合 , 我们把构建请求的逻辑放到 Manager中</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311191321286.png" loading="lazy" class="lazy"></p> <p>当然后续如果需要有 数据持久化 or 关联历史记录 , 再次基础之上修改即可</p> <h3 id="控制层接口"><a href="#控制层接口" class="header-anchor">#</a> 控制层接口</h3> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@PostMapping</span><span class="token punctuation">(</span><span class="token string">&quot;/test/spark&quot;</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">chat</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestParam</span><span class="token punctuation">(</span><span class="token string">&quot;question&quot;</span><span class="token punctuation">)</span> <span class="token class-name">String</span> question<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">try</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> sparkManager<span class="token punctuation">.</span><span class="token function">doChat</span><span class="token punctuation">(</span><span class="token number">132</span><span class="token punctuation">,</span> question<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p><img alt="" data-src="https://pic.yupi.icu/5563/202311191321241.png" loading="lazy" class="lazy"></p> <h2 id="http流式传输-sseemitter"><a href="#http流式传输-sseemitter" class="header-anchor">#</a> HTTP流式传输-SseEmitter</h2> <p>我们在使用官网的大模型对话的时候 , 回答都是一个字一个字响应出来的, 这里我们想达到类似的效果 , 可以采用HTTP推流反馈结果</p> <p>那么上面的代码显然是达不到我们期望的结果的 , 此处可以注意原本 <code>SparkManager.dochat()</code> 中的一个操作</p> <p>这里是使线程等待AI回答问题完毕 , 最后返回</p> <blockquote><p>websocket连接关闭表示 本次的交互已经结束</p></blockquote> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>sparkChat<span class="token punctuation">.</span><span class="token function">getWsCloseFlag</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> sparkChat<span class="token punctuation">.</span><span class="token function">getTotalAnswer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><hr> <p><strong>Spring SseEmitter</strong>主要用于实现服务器端向客户端的实时数据推送。</p> <p>与传统的Http长连接不同，SseEmitter<strong>允许服务器能主动向浏览器推送信息</strong>。</p> <blockquote><p>这种从服务端单向向客户端发送信息的模式，基于Http协议**，相比于WebSocket来说更为轻量**。</p></blockquote> <p>主要功能和用途有以下几个:</p> <ol><li><strong>能主动向单个客户端推送消息</strong>。SseEmitter能匹配唯一的客户端请求，并与该客户端保持持久连接。通过此连接，服务器可以随时将事件推送给这个客户端。</li> <li><strong>能推送重复的消息</strong>。SseEmitter允许服务器不停发送相同的消息给客户端，形成一个连续的事件流。客户端只需要监听这个事件流即可。</li> <li><strong>支持延迟和定时推送</strong>。通过@Scheduled注解，服务器可以在指定时间推送指定延迟的事件。</li> <li><strong>支持推送不同类型的事件</strong>。客户端通过事件的名称能区分不同类型的事件，并作出不同的响应。</li> <li><strong>支持推送基本数据类型和POJO对象</strong>。服务器可以推送String、int等基本类型，也可以推送任意的Java对象。</li> <li><strong>能主动通知客户端关闭</strong>。通过调用complete()或error()方法，服务器可以主动告知客户端连接已关闭。</li> <li><strong>解耦服务器端和客户端</strong>。服务器端仅负责推送事件，与具体的客户端无关。</li></ol> <p>接下来我们来改造原本的SparkManager 以及 控制层代码</p> <p>由于我们需要在控制层返回 AI 回答的信息 , 因此这里添加一个公共的 对象 answer , 原本的SparkChatListener 直接操作这个在控制层中传入的answer</p> <p>同时我们通过 webSocketFlag进行判断 , 轮询 answer是否有变化 , 如果有则通过 emitter 返回给 client</p> <h4 id="核心代码"><a href="#核心代码" class="header-anchor">#</a> 核心代码</h4> <p>这里需要先返回 给客户端 emitter 对象 , 然后通过异步方法来发送 answer信息</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token annotation punctuation">@GetMapping</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token string">&quot;test/spark&quot;</span><span class="token punctuation">,</span> produces <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token class-name">MediaType</span><span class="token punctuation">.</span><span class="token constant">TEXT_EVENT_STREAM_VALUE</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token class-name">SseEmitter</span> <span class="token function">chat</span><span class="token punctuation">(</span><span class="token annotation punctuation">@RequestParam</span><span class="token punctuation">(</span><span class="token string">&quot;question&quot;</span><span class="token punctuation">)</span> <span class="token class-name">String</span> question<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">long</span> userId <span class="token operator">=</span> <span class="token number">132</span><span class="token punctuation">;</span>
    <span class="token keyword">final</span> <span class="token class-name">SseEmitter</span> emitter <span class="token operator">=</span> sparkManager<span class="token punctuation">.</span><span class="token function">getConn</span><span class="token punctuation">(</span>userId<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">CompletableFuture</span><span class="token punctuation">.</span><span class="token function">runAsync</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-&gt;</span><span class="token punctuation">{</span>
        <span class="token class-name">StringBuilder</span> answerCache <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StringBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">SparkChatListener</span> sparkChatListener <span class="token operator">=</span> sparkManager<span class="token punctuation">.</span><span class="token function">doChat</span><span class="token punctuation">(</span>userId<span class="token punctuation">,</span> question<span class="token punctuation">,</span> answerCache<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">int</span> lastIdx <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> len <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>sparkChatListener<span class="token punctuation">.</span><span class="token function">getWsCloseFlag</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">if</span><span class="token punctuation">(</span>lastIdx <span class="token operator">&lt;</span> <span class="token punctuation">(</span>len <span class="token operator">=</span> answerCache<span class="token punctuation">.</span><span class="token function">length</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
                    emitter<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>answerCache<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>lastIdx<span class="token punctuation">,</span> len<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    lastIdx <span class="token operator">=</span> len<span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
                <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span>answerCache<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> emitter<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br></div></div><p>对应的获取emitter 连接的方法放到 SparkManager中</p> <p>这里的key通过一个唯一标识传入即可 (实际的应用可以传入userId 等)</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code>    <span class="token comment">// 用来缓存 用户的SseEmitter</span>
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Object</span><span class="token punctuation">,</span> <span class="token class-name">SseEmitter</span><span class="token punctuation">&gt;</span></span> <span class="token constant">SSE_CACHE</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConcurrentHashMap</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>


<span class="token keyword">public</span> <span class="token class-name">SseEmitter</span> <span class="token function">getConn</span><span class="token punctuation">(</span><span class="token class-name">Object</span> key<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">final</span> <span class="token class-name">SseEmitter</span> sseEmitter <span class="token operator">=</span> <span class="token constant">SSE_CACHE</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>sseEmitter <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> sseEmitter<span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
        <span class="token comment">// 设置连接超时时间，需要配合配置项 spring.mvc.async.request-timeout: 600000 一起使用</span>
        <span class="token keyword">final</span> <span class="token class-name">SseEmitter</span> emitter <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SseEmitter</span><span class="token punctuation">(</span><span class="token number">600000L</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 注册超时回调，超时后触发</span>
        emitter<span class="token punctuation">.</span><span class="token function">onTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-&gt;</span> <span class="token punctuation">{</span>
            log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;连接已超时，正准备关闭，key = {}&quot;</span><span class="token punctuation">,</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token constant">SSE_CACHE</span><span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 注册完成回调，调用 emitter.complete() 触发</span>
        emitter<span class="token punctuation">.</span><span class="token function">onCompletion</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-&gt;</span> <span class="token punctuation">{</span>
            log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;连接已关闭，正准备释放，key = {}&quot;</span><span class="token punctuation">,</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token constant">SSE_CACHE</span><span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
            log<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">&quot;连接已释放，key = {}&quot;</span><span class="token punctuation">,</span> key<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 注册异常回调，调用 emitter.completeWithError() 触发</span>
        emitter<span class="token punctuation">.</span><span class="token function">onError</span><span class="token punctuation">(</span>throwable <span class="token operator">-&gt;</span> <span class="token punctuation">{</span>
            log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">&quot;连接已异常，正准备关闭，key = {}&quot;</span><span class="token punctuation">,</span> key<span class="token punctuation">,</span> throwable<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token constant">SSE_CACHE</span><span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token constant">SSE_CACHE</span><span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> emitter<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> emitter<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br></div></div><p>上面修改的代码相当于把原本在 SparkManager中监听的消息的逻辑放到了控制层的异步方法中 , 因此也需要修改原本的doChat方法</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token class-name">SparkChatListener</span> <span class="token function">doChat</span><span class="token punctuation">(</span><span class="token keyword">long</span> userId<span class="token punctuation">,</span> <span class="token class-name">String</span> question<span class="token punctuation">,</span> <span class="token class-name">StringBuilder</span> answer<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 构建鉴权url</span>
    <span class="token class-name">String</span> authUrl <span class="token operator">=</span> <span class="token class-name">AuthUtil</span><span class="token punctuation">.</span><span class="token function">genAuthUrl</span><span class="token punctuation">(</span>sparkConfig<span class="token punctuation">.</span><span class="token function">getApiKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> sparkConfig<span class="token punctuation">.</span><span class="token function">getApiSecret</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                                         sparkConfig<span class="token punctuation">.</span><span class="token function">getDefaultHostInfo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getHost</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> sparkConfig<span class="token punctuation">.</span><span class="token function">getDefaultHostInfo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>authUrl <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span><span class="token string">&quot;authUrl 生成失败 !&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token class-name">OkHttpClient</span> client <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OkHttpClient<span class="token punctuation">.</span>Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 构建聊天请求</span>
    <span class="token class-name">ChatRequest</span> chatRequest <span class="token operator">=</span> <span class="token function">buildChatRequest</span><span class="token punctuation">(</span>userId<span class="token punctuation">,</span> question<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token class-name">JSONUtil</span><span class="token punctuation">.</span><span class="token function">toJsonStr</span><span class="token punctuation">(</span>chatRequest<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 构建websocket请求</span>
    <span class="token class-name">Request</span> request <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Request<span class="token punctuation">.</span>Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">url</span><span class="token punctuation">(</span>authUrl<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">SparkChatListener</span> sparkChat <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SparkChatListener</span><span class="token punctuation">(</span>answer<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 发起websocket请求</span>
    <span class="token class-name">WebSocket</span> webSocket <span class="token operator">=</span> client<span class="token punctuation">.</span><span class="token function">newWebSocket</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> sparkChat<span class="token punctuation">)</span><span class="token punctuation">;</span>
    webSocket<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token class-name">JSONUtil</span><span class="token punctuation">.</span><span class="token function">toJsonStr</span><span class="token punctuation">(</span>chatRequest<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> sparkChat<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br></div></div><h3 id="测试-2"><a href="#测试-2" class="header-anchor">#</a> 测试</h3> <p>访问 <code>http://localhost:8080/test/spark?question=Spring SseEmitter 是什么?</code></p> <p>可以看到 问题的内容是逐步的做出响应的</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311191321411.png" loading="lazy" class="lazy"></p> <p>如果需要更快的响应 , 可以修改 控制层中 <code>Thread.sleep()</code> 的 参数(越小越灵敏 , 不过频繁的访问也会增大CPU的压力)</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311191321105.png" loading="lazy" class="lazy"></p></div> <footer class="page-edit" style="margin:0;"><div class="edit-link"><a href="https://github.com/liyupi/codefather/edit/master/知识碎片/Websocket.+ Spring-SseEmitter3 实现讯飞星火Java客户端.md" target="_blank" rel="noopener noreferrer">完善页面</a> <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></div> <div class="last-updated"><span class="prefix">最近更新:</span> <span class="time">11/22/2023, 9:15:50 AM</span></div></footer> <!----></div> <div class="toc-container-sidebar"><div class="pos-box"><div class="icon-arrow"></div> <div class="scroll-box" style="max-height:86vh"><div style="font-weight:bold;">Websocket.+ Spring-SseEmitter3 实现讯飞星火Java客户端</div> <hr> <div class="toc-box"><ul class="toc-sidebar-links"><li><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#websocket" class="toc-sidebar-link">websocket</a><ul class="toc-sidebar-sub-headers"><li class="toc-sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#简介" class="toc-sidebar-link">简介</a></li><li class="toc-sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#demo" class="toc-sidebar-link">demo</a></li><li class="toc-sidebar-sub-header toc-sidebar-depth-4"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#websocketserver-java" class="toc-sidebar-link">WebSocketServer.java</a></li><li class="toc-sidebar-sub-header toc-sidebar-depth-4"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#websocketconfig" class="toc-sidebar-link">websocketConfig</a></li><li class="toc-sidebar-sub-header toc-sidebar-depth-4"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#控制层代码" class="toc-sidebar-link">控制层代码</a></li><li class="toc-sidebar-sub-header toc-sidebar-depth-4"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#测试" class="toc-sidebar-link">测试</a></li></ul></li><li><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#讯飞星火" class="toc-sidebar-link">讯飞星火</a><ul class="toc-sidebar-sub-headers"><li class="toc-sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#申请key" class="toc-sidebar-link">申请key</a></li><li class="toc-sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#请求流程分析" class="toc-sidebar-link">请求流程分析</a></li><li class="toc-sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#鉴权-authutil" class="toc-sidebar-link">鉴权-AuthUtil</a></li><li class="toc-sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#构建请求类" class="toc-sidebar-link">构建请求类</a></li><li class="toc-sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#构建结果类" class="toc-sidebar-link">构建结果类</a></li><li class="toc-sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#发起请求" class="toc-sidebar-link">发起请求</a></li><li class="toc-sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#接收响应结果" class="toc-sidebar-link">接收响应结果</a></li><li class="toc-sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#并发访问改造" class="toc-sidebar-link">并发访问改造</a></li><li class="toc-sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#sparkmanager" class="toc-sidebar-link">SparkManager</a></li><li class="toc-sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#控制层接口" class="toc-sidebar-link">控制层接口</a></li></ul></li><li><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#http流式传输-sseemitter" class="toc-sidebar-link">HTTP流式传输-SseEmitter</a><ul class="toc-sidebar-sub-headers"><li class="toc-sidebar-sub-header toc-sidebar-depth-4"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#核心代码" class="toc-sidebar-link">核心代码</a></li><li class="toc-sidebar-sub-header"><a href="/websocket-spring-sseemitter3-%E5%AE%9E%E7%8E%B0%E8%AE%AF%E9%A3%9E%E6%98%9F%E7%81%ABjava%E5%AE%A2%E6%88%B7%E7%AB%AF/#测试-2" class="toc-sidebar-link">测试</a></li></ul></li></ul></div></div></div></div></div>  <main class="footer"> <div class="copy-right"><span class="name"> 编程导航   |     </span> <a target="_blank" rel="noreferrer">
          
        </a></div></main></main> <aside class="page-sidebar"> <div class="page-side-toolbar"></div>  </aside></div><div class="global-ui"><!----></div></div>
    <script src="/assets/js/app.12ab4756.js" defer></script><script src="/assets/js/2.fd06b53e.js" defer></script><script src="/assets/js/73.d6ae6a27.js" defer></script>
  </body>
</html>
